GameMonkey Script

GameMonkey Script Forums
It is currently Wed Aug 15, 2018 1:39 am

All times are UTC




Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: Thread Cloning
PostPosted: Mon May 09, 2011 10:43 pm 
Offline

Joined: Mon Feb 28, 2011 8:06 pm
Posts: 12
In order to support a particular feature, I recently added a Clone method to gmThread. It is mostly the same as the Fork code that is included in the same file, but with some of the efficiency changes I found in these forums. Code is as follows:
Code:
gmThread* gmThread::Clone()
{
   int tid;
   gmThread* result = m_machine->CreateThread(&tid);
   GM_ASSERT(result);

   const gmFunctionObject* funct = GetFunctionObject();
   const int needed_size = ( m_top - m_base + 2 ) + funct->GetMaxStackSize() + funct->GetNumParamsLocals();
   if ( needed_size > result->m_size )
      result->Touch( needed_size - result->m_size );
   // copy stack and vars
   memcpy( result->m_stack, &m_stack[ m_base - 2 ], sizeof( gmVariable ) * (m_top - m_base + 2 ) );

   result->m_frame = m_machine->Sys_AllocStackFrame();
   result->m_frame->m_prev = 0;
   result->m_frame->m_returnAddress = 0;
   result->m_frame->m_returnBase = 0;
   result->m_base = 2;
   result->m_instruction = m_instruction;

   //Match the thread state, so everything is in the right arrays and lists.
   //m_machine->Sys_SwitchState(result,this->GetState());

   return result;
}


So at certain points in my engine code, I take an existing blocked thread, clone it, and signal the clone. My understanding is that this does nothing until the next call to gmMachine::Execute(), when it will be treated as a normal thread.

Most of the time this works as expected. On occasion, however, the cloned thread or some other thread spawned by the clone crashes the vm. The crash occurs in the function gmThread::PushStackFrame(). My understanding is that this code runs when a GameMonkey function call is made. Whenever the crash occurs, the result of the following line of code (about the 13th line of the function) is a NULL.
Code:
gmFunctionObject * fn = (gmFunctionObject *) GM_MOBJECT(m_machine, fnVar->m_value.m_ref);

Inspecting the stack in memory indicates that the type of the gmVariable in question is correctly set to Function, but that its value is indeed NULL.

Given that the GameMonkey script code and functions all work perfectly when I don't use a Clone, all I can think of is that I must have missed some necessary step in setting up the new gmThread. Possibly something to do with memory management that would result in a valid function winding up as a NULL. Though given that the original thread is still sitting in the blocked list, I wouldn't think that any of its referenced variables or functions would have been garbage collected yet...

So. Is there anything obvious that I missed in creating the Clone() function? Can anyone think of a reason why using the Clone() function would result in the random NULLing of gmFunctionObjects on gmThread stacks?


Top
 Profile  
Reply with quote  
 Post subject: Re: Thread Cloning
PostPosted: Thu May 12, 2011 2:55 pm 
Offline

Joined: Mon Feb 28, 2011 8:06 pm
Posts: 12
Solved.

Unsurprisingly, there WAS one step that I missed. The code shown above never sets the TOP value of the stack for the new clone, only the base. Correspondingly, when threads get recycled you can find yourself with an invalid stack state that can run over into the memory of other threads, which is exactly what was happening to me. A recycled clone thread was overwriting the variables of a previously created thread, including function references.

Fortunately, the engine's FORK code does have a line that sets the top. Guess I missed it in my initial copy-paste.

The code to calculate the new top is as follows:
Code:
result->m_top = m_top - m_base + 2;


Which gives us the following Clone code:
Code:
gmThread* gmThread::Clone()
{
   int tid;
   gmThread* result = m_machine->CreateThread(&tid);
   GM_ASSERT(result);

   const gmFunctionObject* funct = GetFunctionObject();
   const int needed_size = ( m_top - m_base + 2 ) + funct->GetMaxStackSize() + funct->GetNumParamsLocals();
   if ( needed_size > result->m_size )
      result->Touch( needed_size - result->m_size );
   // copy stack and vars
   memcpy( result->m_stack, &m_stack[ m_base - 2 ], sizeof( gmVariable ) * (m_top - m_base + 2 ) );

   result->m_frame = m_machine->Sys_AllocStackFrame();
   result->m_frame->m_prev = 0;
   result->m_frame->m_returnAddress = 0;
   result->m_frame->m_returnBase = 0;
   result->m_base = 2;
   result->m_top = m_top - m_base + 2;
   result->m_instruction = m_instruction;

   return result;
}


The only thing that I know I haven't accounted for here is making sure that the thread goes into the correct arrays. By default it goes into the list of running threads, regardless of if the cloned thread is currently running or not. Since I'm starting them up immediately after cloning, it doesn't make a difference to me, but a complete version of Clone would have to take that into account.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group