arturocepeda / Cflat

Embeddable lightweight scripting language with C++ syntax
55 stars 8 forks source link

When reloading a script with env.load, function in environment will have garbage data #14

Open mundusnine opened 7 months ago

mundusnine commented 7 months ago

Here is the end code I ended up with, in which I am nuking the environment and restarting from scratch and that worked

master commit version used: e12b1a0410e28ce67acca109f8c9e203a690a52d

arturocepeda commented 7 months ago

Unfortunately I haven't had the time to watch your stream yet, but I can try to give you some pointers that hopefully will help you get the hot-reloading working.

You should definitely not need to recreate the environment - calling 'load' should do the trick and get the corresponding program replaced in the environment (unless there is something not valid and you get an error, in which case the previous state of the program remains unaltered).

I would suggest that you make sure that the hot-reload happens in a certain phase of the update process in the engine. Doing it either at the beginning or at the end of the frame, for example, would be a clean way to go. Regardless of the file watcher you are using, you could have an array or queue of script reload requests, and then process them all in a deferred way.

Cflat is not thread-safe and doesn't create any additional threads, or anything of the sort, so you shouldn't need to use any synchronization unless you access the environment from different threads in your code. So, one thing that might be happening is that the file watcher you are using notifies you about file changes on a worker thread.

Hope this helps!

mundusnine commented 7 months ago

No need to watch the stream. I just didn't have time to make a reproducible example and instead of having a project of the code I had at least video of the code.

Thanks for the suggestions,but I seem to have already been following them. I re tried putting the exact same code I had last time and here is what I have found out.

If we don't have a lot of code in our function and we do a change, the function will be garbage when reloaded and we will hit: image

image

If we have more calls, it doesn't have garbage in the struct and everything works fine. For my tests, I basically had raylib calls. If I called BeginDraw and EndDraw it wouldn't crash.

Some small code that crashes when we modify the contents of test and call load on file:

//script code in tests.cpp
namespace CfTest
{
    static void Update(){
        const char* test = "Change me and you will crash";// Change this part in the script
        printf("Test, this is a test %s\n",test);
    }
}
//main loop in another .cpp file
void scripts_update(void){
   reload_script();
   Cflat::Environment* env = CflatGlobal::gEnv;
   auto voidFunc = env->getFunction("CfTest::Update");
   env->voidFunctionCall(voidFunc);

}
static int reload_script(void){
  int result = 0;
  const char* filename = "tests.cpp";
  const char* filepath = knob_temp_sprintf("../scripts%s%s", PATH_SEP, filename);
  const char* checkpath = knob_temp_sprintf(BUILD_PATH "%s.temp",filename);
  if(knob_needs_rebuild1(checkpath,filepath)){
     result = CflatGlobal::gEnv->load(filepath);
     if(!result){
        knob_log(KNOB_ERROR,"Failed loading file: %s",filepath);
        knob_log(KNOB_ERROR,"Got Error message: %s", (char*)CflatGlobal::gEnv->getErrorMessage());
     }
     knob_write_entire_file(checkpath,(void*)filepath,strlen(filepath));// <---- Writes to a file to stat against in the future
     break;
  }
  return result;
}

The file changed checking isn't done on another thread. Here you can find the code for knob_needs_rebuild1 if you want to see whats being done.

If ever you still can't recreate it on your side, I can make a simple repo and give you the link so that you could test. I will do some tests on windows(using linux right now) and see if I can recreate it there also.

arturocepeda commented 7 months ago

Alright, thanks for the additional information, I'll take a look!