ecere,ecereCOM: support Emscriptem platform. add new Emscripten interface driver.
[sdk] / ecere / src / net / dcom.ec
1 namespace net;
2
3 #if defined(__WIN32__)
4
5 #define WIN32_LEAN_AND_MEAN
6 #define String _String
7 #include <winsock.h>
8 #undef String
9
10 #elif defined(__unix__) || defined(__APPLE__)
11
12 default:
13 #define set _set
14 #define uint _uint
15 #include <sys/time.h>
16 #include <unistd.h>
17
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <sys/socket.h>
21 #include <sys/wait.h>
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <arpa/inet.h>
25 #undef set
26 #undef uint
27 private:
28
29 typedef int SOCKET;
30 typedef struct hostent HOSTENT;
31 typedef struct sockaddr SOCKADDR;
32 typedef struct sockaddr_in SOCKADDR_IN;
33 typedef struct in_addr IN_ADDR;
34 #define closesocket(s) close(s)
35
36 #endif
37
38 import "network"
39 import "List"
40
41 #if !defined(__EMSCRIPTEN__)
42
43 // SERVER
44
45 static enum DCOMPacketType
46 {
47    dcom_CreateInstance        = 3,
48    dcom_CallMethod            = 5,
49    dcom_CallVirtualMethod     = 6,
50    dcom_InstanceCreated       = 9,
51    dcom_VirtualMethodReturned = 10,
52    dcom_MethodReturned        = 11
53 };
54
55 static class DCOMPacket : Packet
56 {
57    DCOMPacketType type;
58 };
59 static class CreateInstancePacket : DCOMPacket
60 {
61    //int classID;
62    char className[1];
63 };
64
65 static class ObjectCreatedPacket : DCOMPacket
66 {
67    unsigned int objectID;
68 };
69
70 static class CallMethodPacket : DCOMPacket
71 {
72    int objectID;
73    int methodID;
74    int callID;
75    unsigned int argsSize;
76    byte args[1];
77 };
78
79 static class CallVirtualMethodPacket : DCOMPacket
80 {
81    int methodID;
82    int callID;
83    bool hasReturnValue;
84    unsigned int argsSize;
85    byte args[1];
86 };
87
88 static class MethodReturnedPacket : DCOMPacket
89 {
90    int methodID;
91    int callID;
92    unsigned int argsSize;
93    byte args[1];
94 };
95
96 static class VirtualMethodReturnedPacket : DCOMPacket
97 {
98    unsigned int objectID;
99    unsigned int methodID;
100    unsigned int callID;
101    bool overridden;
102    unsigned int argsSize;
103    byte args[1];
104 };
105
106 /*static */class VirtualCallAck : struct
107 {
108 public:
109    int objectID;
110    int methodID;
111    int callID;
112    bool overridden;
113
114    SerialBuffer buffer { };
115 }
116
117 class CallAck : struct
118 {
119 public:
120    int objectID;
121    int methodID;
122    int callID;
123    SerialBuffer buffer { };
124 }
125
126 public class DCOMServerObject
127 {
128    VirtualCallAck VirtualCallAcknowledged(int methodID, int objectID, int callID)
129    {
130       Iterator<VirtualCallAck> it { acks };
131       while(it.Next())
132       {
133          VirtualCallAck ack = it.data;
134          if(ack.methodID == methodID && ack.objectID == objectID && ack.callID == callID)
135          {
136             it.Remove();
137             return ack;
138          }
139       }
140       return null;
141    }
142
143 public:
144    Instance instance;
145
146    virtual void CallMethod(unsigned int __ecereMethodID, SerialBuffer __ecereBuffer);
147
148    dllexport bool CallVirtualMethod(unsigned int methodID, bool hasReturnValue)
149    {
150       bool result = false;
151
152       if(serverSocket && serverSocket.connected)
153       {
154          int64 currentThreadID = GetCurrentThreadID();
155          int callID = nextCallID++;
156          DCOMServerSocket socket = serverSocket;
157          DCOMServerSocket processingSocket;
158          unsigned int size = (uint)(uintptr)&((CallVirtualMethodPacket)0).args + argsBuffer.size; // sizeof(class CallVirtualMethodPacket) + virtualsBuffer.size - 1;
159          CallVirtualMethodPacket packet = (CallVirtualMethodPacket)new0 byte[size];
160          VirtualCallAck ack = null;
161
162          if(currentThreadID == (int64)serverSocket.thread.id)
163             processingSocket = serverSocket;
164          else
165          {
166             processingSocket = null;
167             if(serverSocket.service)
168             {
169                Socket next;
170                for(processingSocket = serverSocket.service.sockets.first;
171                    processingSocket;
172                    processingSocket = (DCOMServerSocket)next)
173                {
174                   next = processingSocket.next;
175                   if(processingSocket.connected &&
176                      (int64)processingSocket.thread.id == currentThreadID)
177                      break;
178                }
179             }
180          }
181
182          packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_CallVirtualMethod);
183          packet.size = size;
184          packet.callID = callID;
185          packet.methodID = htoled(methodID);
186          packet.hasReturnValue = hasReturnValue;
187          packet.argsSize = htoled(argsBuffer.size);
188          argsBuffer.ReadData(packet.args, argsBuffer.size);
189          argsBuffer.Free();
190
191          serverSocket.SendPacket(packet);
192          delete packet;
193
194          socket._refCount += 2;
195          if(processingSocket)
196             processingSocket._refCount += 2;
197
198          incref this;
199
200          while(true)
201          {
202             if(!serverSocket || !serverSocket.connected || !serverSocket.thread)
203                break;
204             if((ack = VirtualCallAcknowledged(methodID, id, callID)))
205                break;
206
207             guiApp.Unlock();
208             mutex.Release();
209             if(processingSocket && processingSocket.connected)
210                processingSocket.ProcessTimeOut(0.01);
211             else
212                ecere::sys::Sleep(0.01);//serverSocket.thread.semaphore.Wait();
213             guiApp.Lock();
214             mutex.Wait();
215          }
216
217          if(ack)
218          {
219             result = ack.overridden;
220             returnBuffer.WriteData(ack.buffer.buffer, ack.buffer.count);
221             delete ack;
222          }
223
224          guiApp.Unlock();
225          mutex.Release();
226
227          if(socket._refCount > 1)
228             socket._refCount--;
229          delete socket;
230
231          if(processingSocket && processingSocket._refCount > 1)
232             processingSocket._refCount--;
233          delete processingSocket;
234
235          guiApp.Lock();
236          mutex.Wait();
237
238          if(_refCount > 1)
239             _refCount--;
240       }
241       return result;
242    }
243 // private:
244    DCOMServerSocket serverSocket;
245    unsigned int id;
246    SerialBuffer argsBuffer { };
247    SerialBuffer returnBuffer { };
248    List<VirtualCallAck> acks { };
249    Mutex mutex { };
250    int nextCallID;
251
252    nextCallID = GetRandom(1, 999999);//100;
253
254    ~DCOMServerObject()
255    {
256       acks.Free();
257    }
258 };
259
260 #define GETLEDWORD(b) (uint32)(((b)[3] << 24) | ((b)[2] << 16) | ((b)[1] << 8) | (b)[0])
261
262 #define PUTLEDWORD(b, d) \
263    (b)[3] = (byte)(((d) >> 24) & 0xFF); \
264    (b)[2] = (byte)(((d) >> 16) & 0xFF); \
265    (b)[1] = (byte)(((d) >> 8)  & 0xFF); \
266    (b)[0] = (byte)( (d)        & 0xFF);
267
268 static uint32 htoled(uint32 value)
269 {
270    uint32 result;
271    PUTLEDWORD((byte *)&result, value);
272    return result;
273 }
274
275 static uint32 letohd(uint32 value)
276 {
277    return GETLEDWORD((byte *)&value);
278 }
279
280 class DCOMServerThread : Thread
281 {
282    DCOMServerSocket socket;
283    Semaphore semaphore { };
284    bool connected;
285
286    unsigned int Main()
287    {
288       incref socket;
289       while(connected)
290       {
291          socket.ProcessTimeOut(0.01);
292          guiApp.Lock();
293          socket.ProcessCalls();
294          guiApp.Unlock();
295          semaphore.Release();
296       }
297       delete socket;
298       return 0;
299    }
300 };
301
302 class DCOMClientThread : Thread
303 {
304    Socket socket;
305    Semaphore semaphore { };
306    bool connected;
307    unsigned int Main()
308    {
309       socket._refCount += 2;
310       while(connected)
311       {
312          socket.ProcessTimeOut(0.01);
313          semaphore.Release();
314       }
315       if(socket._refCount > 1) socket._refCount--;
316       delete socket;
317       return 0;
318    }
319 };
320
321 /*static */public class DCOMServerSocket : Socket
322 {
323    int numObjects;
324    DCOMServerObject * objects;
325    processAlone = true;
326    DCOMServerThread thread
327    {
328       socket = this, connected = true;
329    };
330    List<CallMethodPacket> calls { };
331    bool processingCalls;
332
333    void ProcessCalls()
334    {
335       CallMethodPacket callMethod;
336       Iterator <CallMethodPacket> it { calls };
337       while(true)
338       {
339          mutex.Wait();
340          it.pointer = null;
341          if(!it.Next() || disconnected)
342          {
343             mutex.Release();
344             break;
345          }
346          callMethod = it.data;
347          it.Remove();
348          processingCalls = true;
349          mutex.Release();
350
351          if(callMethod.objectID < numObjects /*&& callMethod.methodID < numMethods*/)
352          {
353             DCOMServerObject object = objects[callMethod.objectID];
354             bool hasReturnValue = true;
355             MethodReturnedPacket packet;
356             unsigned int size;
357             SerialBuffer buffer { };
358             int methodID = callMethod.methodID;
359             int callID = callMethod.callID;
360
361             buffer.WriteData(callMethod.args, callMethod.argsSize);
362
363             if(!hasReturnValue)
364             {
365                size = (uint)(uintptr)&((MethodReturnedPacket)0).args;
366                packet = (MethodReturnedPacket)new0 byte[size];
367                packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_MethodReturned);
368                packet.size = size;
369                packet.callID = callMethod.callID;
370                packet.methodID = callMethod.methodID;
371                packet.argsSize = 0;
372                SendPacket(packet);
373             }
374
375             incref object;
376
377             // TOFIX: Hardcoded VTBL ID
378             ((void (*)(void *, uint, SerialBuffer))(void *)object._vTbl[10])(object, methodID, buffer);
379
380             if(hasReturnValue)
381             {
382                size = (uint)(uintptr)&((MethodReturnedPacket)0).args + buffer.size; // sizeof(class MethodReturnedPacket) + buffer.size - 1;
383                packet = (MethodReturnedPacket)new0 byte[size];
384                packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_MethodReturned);
385                packet.size = size;
386                packet.callID = callID;
387                packet.methodID = methodID;
388                packet.argsSize = htoled(buffer.size);
389                buffer.ReadData(packet.args, buffer.size);
390                SendPacket(packet);
391             }
392             delete buffer;
393             delete packet;
394
395             if(object._refCount > 1)
396                object._refCount--;
397          }
398          delete callMethod;
399          mutex.Wait();
400          processingCalls = false;
401          mutex.Release();
402       }
403    }
404
405    void OnReceivePacket(DCOMPacket packet)
406    {
407       guiApp.Lock();
408       switch((DCOMPacketType)letohd(packet.type))
409       {
410          case dcom_CreateInstance:
411          {
412             CreateInstancePacket createInstance = (CreateInstancePacket)packet;
413             Class _class = eSystem_FindClass(__thisModule.application, createInstance.className);
414             Class runClass = eSystem_FindClass(__thisModule.application, createInstance.className + 4);
415             DCOMServerObject object;
416             int vid;
417
418             if(!_class)
419                _class = eSystem_FindClass(runClass.module, createInstance.className);
420
421             objects = renew objects DCOMServerObject[numObjects+1];
422             object = objects[numObjects] = eInstance_New(_class);
423             incref object;
424             object.serverSocket = this;
425             object.id = numObjects++;
426             object.instance = eInstance_New(runClass);
427             incref object.instance;
428             object.instance._vTbl = new void *[object.instance._class.vTblSize + 1];
429             object.instance._vTbl++;
430             object.instance._vTbl[-1] = (void *)object;
431             memcpy(object.instance._vTbl, object.instance._class._vTbl, sizeof(int(*)()) * object.instance._class.vTblSize);
432             for(vid = runClass.base.vTblSize; vid < runClass.vTblSize; vid++)
433             {
434                object.instance._vTbl[vid] = object._vTbl[vid - runClass.base.vTblSize + 11];
435             }
436
437             {
438                ObjectCreatedPacket sendPacket = ObjectCreatedPacket { };
439                sendPacket.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_InstanceCreated);
440                sendPacket.size = sizeof(class ObjectCreatedPacket);
441                sendPacket.objectID = htoled(object.id);
442                SendPacket(sendPacket);
443                delete sendPacket;
444             }
445             break;
446          }
447          case dcom_CallMethod:
448          {
449             CallMethodPacket p;
450             CallMethodPacket callMethod = (CallMethodPacket)packet;
451             callMethod.objectID = letohd(callMethod.objectID);
452             callMethod.argsSize = letohd(callMethod.argsSize);
453             mutex.Wait();
454             p = (CallMethodPacket)new byte[callMethod.size];
455             memcpy(p, callMethod, callMethod.size);
456             calls.Add(p);
457             mutex.Release();
458             break;
459          }
460          case dcom_VirtualMethodReturned:
461          {
462             VirtualMethodReturnedPacket methodReturned = (VirtualMethodReturnedPacket)packet;
463             if(methodReturned.objectID < numObjects)
464             {
465                DCOMServerObject object = objects[methodReturned.objectID];
466                VirtualCallAck ack
467                {
468                   methodReturned.objectID,
469                   methodReturned.methodID,
470                   methodReturned.callID,
471                   methodReturned.overridden
472                };
473                ack.buffer.WriteData(methodReturned.args, letohd(methodReturned.argsSize));
474                mutex.Wait();
475                object.acks.Add(ack);
476                mutex.Release();
477             }
478             break;
479          }
480       }
481       guiApp.Unlock();
482    }
483
484    void OnDisconnect(int code)
485    {
486       guiApp.Lock();
487       thread.connected = false;
488       guiApp.Unlock();
489       if(thread.started && GetCurrentThreadID() != (int64)thread.id)
490          thread.Wait();
491    }
492
493    ~DCOMServerSocket()
494    {
495       int c;
496       guiApp.Lock();
497       mutex.Wait();
498       for(c = 0; c<numObjects; c++)
499       {
500          objects[c].instance._vTbl--;
501          delete objects[c].instance._vTbl;
502          objects[c].instance._vTbl = objects[c].instance._class._vTbl;
503          delete objects[c].instance;
504          delete objects[c];
505       }
506       delete objects;
507       mutex.Release();
508       guiApp.Unlock();
509    }
510 };
511
512 class DCOMServiceThread : Thread
513 {
514    DCOMService service;
515    bool connected;
516    unsigned int Main()
517    {
518       DCOMService service = this.service;
519       incref service;
520       while(connected)
521       {
522          service.Process();
523       }
524       delete service;
525       return 0;
526    }
527 };
528
529 public class DCOMService : Service
530 {
531    port = 3114;
532    processAlone = true;
533    DCOMServiceThread thread
534    {
535       service = this, connected = true;
536    };
537    ~DCOMService()
538    {
539       if(thread.started && GetCurrentThreadID() != (int64)thread.id)
540          thread.Wait();
541    }
542
543    public bool Start()
544    {
545       bool result = Service::Start();
546       if(result)
547       {
548          thread.connected = true;
549          thread.Create();
550       }
551       return result;
552    }
553
554    public bool Stop()
555    {
556       bool result = true;
557       thread.connected = false;
558       result = Service::Stop();
559       if(thread.started && GetCurrentThreadID() != (int64)thread.id)
560          thread.Wait();
561       return result;
562    }
563
564    void OnAccept()
565    {
566       DCOMServerSocket socket { };
567       incref socket;
568       socket.service = this;
569       if(socket.connected)
570          socket.thread.Create();
571       delete socket;
572    }
573 };
574
575 // CLIENT
576 public class DCOMClientObject : Socket
577 {
578    CallAck CallAcknowledged(int methodID, int objectID, int callID)
579    {
580       Iterator<CallAck> it { acks };
581       while(it.Next())
582       {
583          CallAck ack = it.data;
584          if(ack.methodID == methodID && ack.objectID == objectID && ack.callID == callID)
585          {
586             it.Remove();
587             return ack;
588          }
589       }
590       return null;
591    }
592
593 public:
594    unsigned int objectID;
595    bool answered;
596    SerialBuffer __ecereBuffer { };
597    List<CallAck> acks { };
598    int nextCallID;
599
600    nextCallID = GetRandom(1, 999999);
601
602    processAlone = true;
603    private DCOMClientThread thread
604    {
605       socket = this, connected = true;
606    };
607
608    bool Connect(const char * server, int port)
609    {
610       bool result = false;
611       if(Socket::Connect(server, port))
612       {
613          int len = (int)(strlen(_class.name) + 4 - strlen("DCOMClient_"));
614          unsigned int size = sizeof(class CreateInstancePacket) + len;
615          CreateInstancePacket packet = (CreateInstancePacket)new0 byte[size];
616          packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_CreateInstance);
617          packet.size = size;
618          CopyBytes(packet.className, "DCOM", 4);
619          CopyBytes(packet.className + 4, _class.name + strlen("DCOMClient_"), len-4+1);
620          answered = false;
621          SendPacket(packet);
622          delete packet;
623          thread.socket = this;
624          thread.connected = true;
625          thread.Create();
626          guiApp.Unlock();
627          while(!answered && thread && connected)
628          {
629             //guiApp.WaitNetworkEvent();
630             //guiApp.ProcessNetworkEvents();
631             // Process();
632
633             if(GetCurrentThreadID() == (int64)thread.id)
634                Process();
635             else
636                thread.semaphore.Wait();
637          }
638          guiApp.Lock();
639          result = connected;
640       }
641       return result;
642    }
643
644    virtual void CallVirtualMethod(unsigned int __ecereMethodID, SerialBuffer __ecereBuffer);
645
646    void OnReceivePacket(DCOMPacket p)
647    {
648       guiApp.Lock();
649       if(connected)
650       {
651          switch((DCOMPacketType)letohd(p.type))
652          {
653             case dcom_InstanceCreated:
654             {
655                ObjectCreatedPacket packet = (ObjectCreatedPacket)p;
656                objectID = letohd(packet.objectID);
657                answered = true;
658                break;
659             }
660             case dcom_MethodReturned:
661             {
662                MethodReturnedPacket packet = (MethodReturnedPacket)p;
663                CallAck ack
664                {
665                   objectID,
666                   packet.methodID,
667                   packet.callID
668                };
669                ack.buffer.WriteData(packet.args, letohd(packet.argsSize));
670                acks.Add(ack);
671                break;
672             }
673             // Virtual Method Called
674             case dcom_CallVirtualMethod:
675             {
676                CallVirtualMethodPacket callMethod = (CallVirtualMethodPacket)p;
677                VirtualMethodReturnedPacket packet = null;
678                unsigned int size = (uint)(uintptr)&((VirtualMethodReturnedPacket)0).args; // sizeof(class VirtualMethodReturnedPacket);
679                SerialBuffer buffer { };
680                bool hasReturnValue = callMethod.hasReturnValue;
681                int methodID = callMethod.methodID;
682                int callID = callMethod.callID;
683                // TOFIX: Hardcoded VTBL ID
684                bool overridden = _vTbl[18 + methodID] != _class._vTbl[18 + methodID];
685                callMethod.argsSize = letohd(callMethod.argsSize);
686
687                if(!hasReturnValue)
688                {
689                   packet = (VirtualMethodReturnedPacket)new0 byte[size];
690                   packet.overridden = overridden;
691                   packet.objectID = objectID;
692                   packet.methodID = methodID;
693                   packet.callID = callID;
694                   packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_VirtualMethodReturned);
695                   packet.size = size;
696                   packet.argsSize = 0;
697                   SendPacket(packet);
698                }
699
700                if(overridden)
701                {
702                   buffer.WriteData(callMethod.args, callMethod.argsSize);
703
704                   // TOFIX: Hardcoded VTBL ID
705                   ((void (*)(void *, uint, SerialBuffer))(void *)_vTbl[17])(this, callMethod.methodID, buffer);
706
707                   // WARNING: callMethod packet is invalidated !!!
708
709                   size += buffer.size; // - 1;
710                }
711                if(hasReturnValue)
712                {
713                   packet = (VirtualMethodReturnedPacket)new0 byte[size];
714                   packet.overridden = overridden;
715                   packet.objectID = objectID;
716                   packet.methodID = methodID;
717                   packet.callID = callID;
718                   packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_VirtualMethodReturned);
719                   packet.size = size;
720                   packet.argsSize = htoled(buffer.size);
721                   buffer.ReadData(packet.args, buffer.size);
722                   SendPacket(packet);
723                }
724                delete buffer;
725                delete packet;
726                break;
727             }
728          }
729          guiApp.Unlock();
730       }
731    }
732
733    void OnDisconnect(int code)
734    {
735       if(thread)
736          thread.connected = false;
737       answered = true; //2;
738    }
739
740    dllexport bool CallMethod(unsigned int methodID)
741    {
742       bool result = false;
743       if(this && connected)
744       {
745          CallAck ack = null;
746          int callID = nextCallID++;
747          unsigned int size = (uint)(uintptr)&((CallMethodPacket)0).args + __ecereBuffer.size; // sizeof(class CallMethodPacket) + __ecereBuffer.size - 1;
748          CallMethodPacket packet = (CallMethodPacket)new0 byte[size];
749          packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_CallMethod);
750          packet.size = size;
751          packet.objectID = htoled(objectID);
752          packet.methodID = htoled(methodID);
753          packet.callID = callID;
754          packet.argsSize = htoled(__ecereBuffer.size);
755          __ecereBuffer.ReadData(packet.args, __ecereBuffer.size);
756          SendPacket(packet);
757          delete packet;
758
759          while(true)
760          {
761             if(!thread || !connected)
762                break;
763             if((ack = CallAcknowledged(methodID, objectID, callID)))
764                break;
765             guiApp.Unlock();
766
767             //guiApp.WaitNetworkEvent();
768             //guiApp.ProcessNetworkEvents();
769             //Process();
770             if(GetCurrentThreadID() == (int64)thread.id)
771                ProcessTimeOut(0.01);
772             else
773                ecere::sys::Sleep(0.01);//thread.semaphore.Wait();
774             guiApp.Lock();
775          }
776
777          if(ack)
778          {
779             __ecereBuffer.Free();
780             __ecereBuffer.WriteData(ack.buffer.buffer, ack.buffer.count);
781             delete ack;
782             result = true;
783          }
784       }
785       return result;
786    }
787
788    ~DCOMClientObject()
789    {
790       if(thread.started)
791       {
792          if(GetCurrentThreadID() != (int64)thread.id)
793             thread.Wait();
794       }
795       acks.Free();
796    }
797 };
798
799 // A class to make set of things happen atomically
800 // e.g. to send a bunch of notifications all at once,
801 // without anything else happening in between
802 public class DCOMSendControl
803 {
804    bool sendingOut;
805 public:
806    void Stop()
807    {
808       while(sendingOut) guiApp.Unlock(), ecere::sys::Sleep(0.01), guiApp.Lock();
809       sendingOut = true;
810    }
811
812    void Resume()
813    {
814       sendingOut = false;
815    }
816 }
817
818 #endif // !defined(__EMSCRIPTEN__)