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