ecere/net/dcom; compiler/ecs: Improvements to solve interlock and racing conditions...
authorJerome St-Louis <jerome@ecere.com>
Mon, 30 Jan 2012 12:45:54 +0000 (19:45 +0700)
committerJerome St-Louis <jerome@ecere.com>
Mon, 30 Jan 2012 12:45:54 +0000 (19:45 +0700)
- Solved interlock by letting virtual methods that do not return information, return before going into user code
  This way all 4 players can be notified before one such notification ends up waiting on something that will get done
  after notifying all players is completed.
- Solved racing condition involving Server object's virtualsBuffer by locking a mutex around its use
- DCOM bindings generation had to be modified in ecs

compiler/ecs/ecs.ec
ecere/src/net/dcom.ec

index facd59f..f9aa228 100644 (file)
@@ -1213,6 +1213,7 @@ static void BindDCOMClient()
 
 static void BindDCOMServer()
 {
+   bool mutexDeclared = false;
    Class _class;
    for(_class = privateModule.classes.first; _class; _class = _class.next)
    {
@@ -1424,6 +1425,20 @@ static void BindDCOMServer()
                      break;
                if(method)
                {
+                  if(!mutexDeclared)
+                  {
+                     DeclareClass(FindClass("ecere::sys::Mutex"), "__ecereClass___ecereNameSpace__ecere__sys__Mutex");
+                     DeclareMethod(
+                        eClass_FindMethod(
+                           eSystem_FindClass(privateModule, "ecere::sys::Mutex"), "Wait", privateModule), 
+                              "__ecereMethod___ecereNameSpace__ecere__sys__Mutex_Wait");
+                     DeclareMethod(
+                        eClass_FindMethod(
+                           eSystem_FindClass(privateModule, "ecere::sys::Mutex"), "Release", privateModule), 
+                              "__ecereMethod___ecereNameSpace__ecere__sys__Mutex_Release");
+                     mutexDeclared = true;
+                  }
+
                   f.Printf("\n");
                   if(!method.dataType)
                      method.dataType = ProcessTypeString(method.dataTypeString, false);
@@ -1474,6 +1489,9 @@ static void BindDCOMServer()
                               f.Printf(" = 0");
                            f.Printf(";\n\n");
                         }
+
+                        f.Printf("      __ecereMethod___ecereNameSpace__ecere__sys__Mutex_Wait(__ecereObject.mutex);\n");
+
                         //f.Printf("      incref this;\n");
                         for(param = method.dataType.params.first; param; param = param.next)
                         {
@@ -1506,9 +1524,22 @@ static void BindDCOMServer()
                         DeclareMethod(
                            eClass_FindMethod(
                               eSystem_FindClass(privateModule, "ecere::net::DCOMServerObject"), "CallVirtualMethod", privateModule), 
-                           "__ecereMethod___ecereNameSpace__ecere__net__DCOMServerObject_CallVirutalMethod");
+                           "__ecereMethod___ecereNameSpace__ecere__net__DCOMServerObject_CallVirtualMethod");
 
-                        f.Printf("      if(__ecereObject.CallVirtualMethod(%d))\n", vid - _class.base.vTblSize);
+                        // Check if this method needs to return anything (hasReturnValue)
+                        {
+                           bool hasReturnValue = false;
+                           for(param = method.dataType.params.first; param; param = param.next)
+                           {
+                              if(param.kind == classType && ((param._class && param._class.registered && param._class.registered.type == structClass) || !strcmp(param._class.string, "String")) && !param.constant)
+                              {
+                                 hasReturnValue = true;
+                                 break;
+                              }
+                           }
+                           f.Printf("      if(__ecereObject.CallVirtualMethod(%d, %s))\n", vid - _class.base.vTblSize,
+                              hasReturnValue ? "true" : "false");
+                        }
                         f.Printf("      {\n");
                         for(param = method.dataType.params.first; param; param = param.next)
                         {
@@ -1544,6 +1575,7 @@ static void BindDCOMServer()
                         f.Printf(");\n");
 
                         f.Printf("      __ecereObject.virtualsBuffer.Free();\n");
+                        f.Printf("      __ecereMethod___ecereNameSpace__ecere__sys__Mutex_Release(__ecereObject.mutex);\n");
                         //f.Printf("      delete this;\n");
                         if(method.dataType.returnType.kind != voidType)
                         {
index f956f3e..648f9b8 100644 (file)
@@ -72,6 +72,7 @@ static class CallMethodPacket : DCOMPacket
 static class CallVirtualMethodPacket : DCOMPacket
 {
    int methodID;
+   bool hasReturnValue;
    unsigned int argsSize;
    byte args[1];
 };
@@ -97,17 +98,20 @@ public:
 
    virtual void CallMethod(unsigned int __ecereMethodID, SerialBuffer __ecereBuffer);
 
-   dllexport bool CallVirtualMethod(unsigned int methodID)
+   dllexport bool CallVirtualMethod(unsigned int methodID, bool hasReturnValue)
    {
       bool reentrant = !answered;
-      guiApp.Unlock();
+
       if(serverSocket && serverSocket.connected)
       {
          unsigned int size = (uint)&((CallVirtualMethodPacket)0).args + virtualsBuffer.size; // sizeof(class CallVirtualMethodPacket) + virtualsBuffer.size - 1;
          CallVirtualMethodPacket packet = (CallVirtualMethodPacket)new0 byte[size];
+         guiApp.Unlock();
+
          packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_CallVirtualMethod);
          packet.size = size;
          packet.methodID = htoled(methodID);
+         packet.hasReturnValue = hasReturnValue;
          packet.argsSize = htoled(virtualsBuffer.size);
          virtualsBuffer.ReadData(packet.args, virtualsBuffer.size);
          answered = false;
@@ -115,6 +119,8 @@ public:
          serverSocket.SendPacket(packet);
          delete packet;
 
+         mutex.Release();
+
          while(serverSocket && !answered)
          {
             if(GetCurrentThreadID() == serverSocket.thread.id)
@@ -122,12 +128,17 @@ public:
             else
                serverSocket.thread.semaphore.Wait();
          }
+
+         // NOTE: There is a slight potential that a virtual method
+         // returning a value might have its virtualsBuffer modified
+         // before we manage to lock it...
+         guiApp.Lock();
+         mutex.Wait();
+
          if(reentrant)
             answered = false;
-         guiApp.Lock();
          return overridden == true;
       }
-      guiApp.Lock();
       return false;
    }
 // private:
@@ -136,6 +147,7 @@ public:
    SerialBuffer buffer { };
    SerialBuffer virtualsBuffer { };
    bool answered, overridden;
+   Mutex mutex { };
 
    answered = true;
 };
@@ -257,7 +269,7 @@ class DCOMClientThread : Thread
 
                MethodReturnedPacket packet;
                unsigned int size;
-               SerialBuffer buffer = object.buffer;
+               SerialBuffer buffer { };
 
                buffer.WriteData(callMethod.args, callMethod.argsSize);
                // TOFIX: Hardcoded VTBL ID
@@ -269,7 +281,7 @@ class DCOMClientThread : Thread
                packet.size = size;
                packet.argsSize = htoled(buffer.size);
                buffer.ReadData(packet.args, buffer.size);
-               buffer.Free();
+               delete buffer;
                SendPacket(packet);
                delete packet;
             }
@@ -454,26 +466,42 @@ public:
             CallVirtualMethodPacket callMethod = (CallVirtualMethodPacket)p;
             VirtualMethodReturnedPacket packet;
             unsigned int size = (uint)&((VirtualMethodReturnedPacket)0).args; // sizeof(class VirtualMethodReturnedPacket);
-            SerialBuffer buffer = virtualsBuffer;
+            SerialBuffer buffer { };
             // TOFIX: Hardcoded VTBL ID
             bool overridden = _vTbl[18 + callMethod.methodID] != _class._vTbl[18 + callMethod.methodID];
             callMethod.argsSize = letohd(callMethod.argsSize);
+
+            if(!callMethod.hasReturnValue)
+            {
+               packet = (VirtualMethodReturnedPacket)new0 byte[size];
+               packet.overridden = overridden;
+               packet.objectID = objectID;
+               packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_VirtualMethodReturned);
+               packet.size = size;
+               packet.argsSize = 0;
+               SendPacket(packet);
+            }
+
             if(overridden)
             {
                buffer.WriteData(callMethod.args, callMethod.argsSize);
+
                // TOFIX: Hardcoded VTBL ID
                _vTbl[17](this, callMethod.methodID, buffer);
                size += buffer.size; // - 1;
             }
-            packet = (VirtualMethodReturnedPacket)new0 byte[size];
-            packet.overridden = overridden;
-            packet.objectID = objectID;
-            packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_VirtualMethodReturned);
-            packet.size = size;
-            packet.argsSize = htoled(buffer.size);
-            buffer.ReadData(packet.args, buffer.size);
-            buffer.Free();
-            SendPacket(packet);
+            if(callMethod.hasReturnValue)
+            {
+               packet = (VirtualMethodReturnedPacket)new0 byte[size];
+               packet.overridden = overridden;
+               packet.objectID = objectID;
+               packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_VirtualMethodReturned);
+               packet.size = size;
+               packet.argsSize = htoled(buffer.size);
+               buffer.ReadData(packet.args, buffer.size);
+               SendPacket(packet);
+            }
+            delete buffer;
             delete packet;
             break;
          }