ecere/sys/GetCurrentThredID(): Changed to return a 64 bit type
[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 static WSADATA wsaData;
10
11 #elif defined(__unix__) || defined(__APPLE__)
12
13 default:
14 #define set _set
15 #define uint _uint
16 #include <sys/time.h>
17 #include <unistd.h>
18
19 #include <netinet/in.h>
20 #include <netdb.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23 #include <sys/types.h>
24 #include <sys/time.h>
25 #include <arpa/inet.h>
26 #undef set
27 #undef uint
28 private:
29
30 typedef int SOCKET;
31 typedef struct hostent HOSTENT;
32 typedef struct sockaddr SOCKADDR;
33 typedef struct sockaddr_in SOCKADDR_IN;
34 typedef struct in_addr IN_ADDR;
35 #define closesocket(s) close(s)
36
37 #endif
38
39 import "network"
40 import "List"
41
42 // SERVER
43
44 static enum DCOMPacketType
45 {
46    dcom_CreateInstance        = 3,
47    dcom_CallMethod            = 5,
48    dcom_CallVirtualMethod     = 6,
49    dcom_InstanceCreated       = 9,
50    dcom_VirtualMethodReturned = 10,
51    dcom_MethodReturned        = 11
52 };
53
54 static class DCOMPacket : Packet
55 {
56    DCOMPacketType type;
57 };
58 static class CreateInstancePacket : DCOMPacket
59 {
60    //int classID;
61    char className[1];
62 };
63
64 static class ObjectCreatedPacket : DCOMPacket
65 {
66    unsigned int objectID;
67 };
68
69 static class CallMethodPacket : DCOMPacket
70 {
71    int objectID;
72    int methodID;
73    int callID;
74    unsigned int argsSize;
75    byte args[1];
76 };
77
78 static class CallVirtualMethodPacket : DCOMPacket
79 {
80    int methodID;
81    int callID;
82    bool hasReturnValue;
83    unsigned int argsSize;
84    byte args[1];
85 };
86
87 static class MethodReturnedPacket : DCOMPacket
88 {
89    int methodID;
90    int callID;
91    unsigned int argsSize;
92    byte args[1];
93 };
94
95 static class VirtualMethodReturnedPacket : DCOMPacket
96 {
97    unsigned int objectID;
98    unsigned int methodID;
99    unsigned int callID;
100    bool overridden;
101    unsigned int argsSize;
102    byte args[1];
103 };
104
105 /*static */class VirtualCallAck : struct
106 {
107 public:
108    int objectID;
109    int methodID;
110    int callID;
111    bool overridden;
112
113    SerialBuffer buffer { };
114 }
115
116 class CallAck : strict
117 {
118 public:
119    int objectID;
120    int methodID;
121    int callID;
122    SerialBuffer buffer { };
123 }
124
125 public class DCOMServerObject
126 {
127    VirtualCallAck VirtualCallAcknowledged(int methodID, int objectID, int callID)
128    {
129       Iterator<VirtualCallAck> it { acks };
130       while(it.Next())
131       {
132          VirtualCallAck ack = it.data;
133          if(ack.methodID == methodID && ack.objectID == objectID && ack.callID == callID)
134          {
135             it.Remove();
136             return ack;
137          }
138       }
139       return null;
140    }
141
142 public:
143    Instance instance;
144
145    virtual void CallMethod(unsigned int __ecereMethodID, SerialBuffer __ecereBuffer);
146
147    dllexport bool CallVirtualMethod(unsigned int methodID, bool hasReturnValue)
148    {
149       bool result = false;
150
151       if(serverSocket && serverSocket.connected)
152       {
153          int64 currentThreadID = GetCurrentThreadID();
154          int callID = nextCallID++;
155          DCOMServerSocket socket = serverSocket;
156          DCOMServerSocket processingSocket;
157          unsigned int size = (uint)&((CallVirtualMethodPacket)0).args + argsBuffer.size; // sizeof(class CallVirtualMethodPacket) + virtualsBuffer.size - 1;
158          CallVirtualMethodPacket packet = (CallVirtualMethodPacket)new0 byte[size];
159          VirtualCallAck ack = null;
160
161          if(currentThreadID == serverSocket.thread.id)
162             processingSocket = serverSocket;
163          else
164          {
165             processingSocket = null;
166             if(serverSocket.service)
167             {
168                Socket next;
169                for(processingSocket = serverSocket.service.sockets.first;
170                    processingSocket;
171                    processingSocket = (DCOMServerSocket)processingSocket.next)
172                {
173                   next = processingSocket.next;
174                   if(processingSocket.connected && 
175                      processingSocket.thread.id == currentThreadID)
176                      break;
177                }
178             }
179          }
180
181          packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_CallVirtualMethod);
182          packet.size = size;
183          packet.callID = callID;
184          packet.methodID = htoled(methodID);
185          packet.hasReturnValue = hasReturnValue;
186          packet.argsSize = htoled(argsBuffer.size);
187          argsBuffer.ReadData(packet.args, argsBuffer.size);
188          argsBuffer.Free();
189
190          serverSocket.SendPacket(packet);
191          delete packet;
192
193          socket._refCount += 2;
194          if(processingSocket)
195             processingSocket._refCount += 2;
196
197          incref this;
198
199          while(true)
200          {
201             if(!serverSocket || !serverSocket.connected || !serverSocket.thread)
202                break;
203             if(ack = VirtualCallAcknowledged(methodID, id, callID))
204                break;
205
206             guiApp.Unlock();
207             mutex.Release();
208             if(processingSocket && processingSocket.connected)
209                processingSocket.ProcessTimeOut(0.01);
210             else
211                ecere::sys::Sleep(0.01);//serverSocket.thread.semaphore.Wait();
212             guiApp.Lock();
213             mutex.Wait();
214          }
215
216          if(ack)
217          {
218             result = ack.overridden;
219             returnBuffer.WriteData(ack.buffer.buffer, ack.buffer.count);
220             delete ack;
221          }
222
223          guiApp.Unlock();
224          mutex.Release();
225
226          if(socket._refCount > 1)
227             socket._refCount--;
228          delete socket;
229
230          if(processingSocket && processingSocket._refCount > 1)
231             processingSocket._refCount--;
232          delete processingSocket;
233
234          guiApp.Lock();
235          mutex.Wait();
236
237          if(_refCount > 1)
238             _refCount--;
239       }
240       return result;
241    }
242 // private:
243    DCOMServerSocket serverSocket;
244    unsigned int id;
245    SerialBuffer argsBuffer { };
246    SerialBuffer returnBuffer { };
247    List<VirtualCallAck> acks { };
248    Mutex mutex { };
249    int nextCallID;
250
251    nextCallID = GetRandom(1, 999999);//100;
252
253    ~DCOMServerObject()
254    {
255       acks.Free();
256    }
257 };
258
259 #define GETLEDWORD(b) (uint32)(((b)[3] << 24) | ((b)[2] << 16) | ((b)[1] << 8) | (b)[0])
260
261 #define PUTLEDWORD(b, d) \
262    (b)[3] = (byte)(((d) >> 24) & 0xFF); \
263    (b)[2] = (byte)(((d) >> 16) & 0xFF); \
264    (b)[1] = (byte)(((d) >> 8)  & 0xFF); \
265    (b)[0] = (byte)( (d)        & 0xFF);
266
267 static uint32 htoled(uint32 value)
268 {
269    uint32 result;
270    PUTLEDWORD((byte *)&result, value);
271    return result;
272 }
273
274 static uint32 letohd(uint32 value)
275 {
276    return GETLEDWORD((byte *)&value);   
277 }
278
279 class DCOMServerThread : Thread
280 {
281    DCOMServerSocket socket;
282    Semaphore semaphore { };
283    bool connected;
284
285    unsigned int Main()
286    {
287       incref socket;
288       while(connected)
289       {
290          socket.ProcessTimeOut(0.01);
291          guiApp.Lock();
292          socket.ProcessCalls();
293          guiApp.Unlock();
294          semaphore.Release();
295       }
296       delete socket;
297       return 0;
298    }
299 };
300
301 class DCOMClientThread : Thread
302 {
303    Socket socket;
304    Semaphore semaphore { };
305    bool connected;
306    unsigned int Main()
307    {
308       socket._refCount += 2;
309       while(connected)
310       {
311          socket.ProcessTimeOut(0.01);
312          semaphore.Release();
313       }
314       if(socket._refCount > 1) socket._refCount--;
315       delete socket;
316       return 0;
317    }
318 };
319
320 /*static */public class DCOMServerSocket : Socket
321 {
322    int numObjects;
323    DCOMServerObject * objects;
324    processAlone = true;
325    DCOMServerThread thread
326    {
327       socket = this, connected = true;
328    };
329    List<CallMethodPacket> calls { };
330    bool processingCalls;
331
332    void ProcessCalls()
333    {
334       CallMethodPacket callMethod;
335       Iterator <CallMethodPacket> it { calls };
336       while(true)
337       {
338          mutex.Wait();
339          it.pointer = null;
340          if(!it.Next() || disconnected)
341          {
342             mutex.Release();
343             break;
344          }
345          callMethod = it.data;
346          it.Remove();
347          processingCalls = true;
348          mutex.Release();
349
350          if(callMethod.objectID < numObjects /*&& callMethod.methodID < numMethods*/)
351          {
352             DCOMServerObject object = objects[callMethod.objectID];
353             bool hasReturnValue = true;
354             MethodReturnedPacket packet;
355             unsigned int size;
356             SerialBuffer buffer { };
357             int methodID = callMethod.methodID;
358             int callID = callMethod.callID;
359
360             buffer.WriteData(callMethod.args, callMethod.argsSize);
361
362             if(!hasReturnValue)
363             {
364                size = (uint)&((MethodReturnedPacket)0).args;
365                packet = (MethodReturnedPacket)new0 byte[size];
366                packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_MethodReturned);
367                packet.size = size;
368                packet.callID = callMethod.callID;
369                packet.methodID = callMethod.methodID;
370                packet.argsSize = 0;
371                SendPacket(packet);
372             }
373
374             incref object;
375
376             // TOFIX: Hardcoded VTBL ID
377             ((void (*)(void *, uint, SerialBuffer))(void *)object._vTbl[10])(object, methodID, buffer);
378
379             if(hasReturnValue)
380             {
381                size = (uint)&((MethodReturnedPacket)0).args + buffer.size; // sizeof(class MethodReturnedPacket) + buffer.size - 1;
382                packet = (MethodReturnedPacket)new0 byte[size];
383                packet.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_MethodReturned);
384                packet.size = size;
385                packet.callID = callID;
386                packet.methodID = methodID;
387                packet.argsSize = htoled(buffer.size);
388                buffer.ReadData(packet.args, buffer.size);
389                SendPacket(packet);
390             }
391             delete buffer;
392             delete packet;
393
394             if(object._refCount > 1)
395                object._refCount--;
396          }
397          delete callMethod;
398          mutex.Wait();
399          processingCalls = false;
400          mutex.Release();
401       }
402    }
403
404    void OnReceivePacket(DCOMPacket packet)
405    {
406       guiApp.Lock();
407       switch((DCOMPacketType)letohd(packet.type))
408       {
409          case dcom_CreateInstance:
410          {
411             CreateInstancePacket createInstance = (CreateInstancePacket)packet;
412             Class _class = eSystem_FindClass(__thisModule.application, createInstance.className);
413             Class runClass = eSystem_FindClass(__thisModule.application, createInstance.className + 4);
414             DCOMServerObject object;
415             int vid;
416             
417             if(!_class)
418                _class = eSystem_FindClass(runClass.module, createInstance.className);
419             
420             objects = renew objects DCOMServerObject[numObjects+1];
421             object = objects[numObjects] = eInstance_New(_class);
422             incref object;
423             object.serverSocket = this;
424             object.id = numObjects++;
425             object.instance = eInstance_New(runClass);   
426             incref object.instance;
427             object.instance._vTbl = new void *[object.instance._class.vTblSize + 1];
428             object.instance._vTbl++;
429             object.instance._vTbl[-1] = (void *)object;
430             memcpy(object.instance._vTbl, object.instance._class._vTbl, sizeof(int(*)()) * object.instance._class.vTblSize);
431             for(vid = runClass.base.vTblSize; vid < runClass.vTblSize; vid++)
432             {
433                object.instance._vTbl[vid] = object._vTbl[vid - runClass.base.vTblSize + 11];
434             }
435
436             {
437                ObjectCreatedPacket sendPacket = ObjectCreatedPacket { };
438                sendPacket.type = (DCOMPacketType)htoled((DCOMPacketType)dcom_InstanceCreated);
439                sendPacket.size = sizeof(class ObjectCreatedPacket);
440                sendPacket.objectID = htoled(object.id);
441                SendPacket(sendPacket);
442                delete sendPacket;
443             }
444             break;
445          }
446          case dcom_CallMethod:
447          {
448             CallMethodPacket p;
449             CallMethodPacket callMethod = (CallMethodPacket)packet;
450             callMethod.objectID = letohd(callMethod.objectID);
451             callMethod.argsSize = letohd(callMethod.argsSize);
452             mutex.Wait();
453             p = (CallMethodPacket)new byte[callMethod.size];
454             memcpy(p, callMethod, callMethod.size);
455             calls.Add(p);
456             mutex.Release();
457             break;
458          }
459          case dcom_VirtualMethodReturned:
460          {
461             VirtualMethodReturnedPacket methodReturned = (VirtualMethodReturnedPacket)packet;
462             if(methodReturned.objectID < numObjects)
463             {
464                DCOMServerObject object = objects[methodReturned.objectID];
465                VirtualCallAck ack
466                {
467                   methodReturned.objectID,
468                   methodReturned.methodID,
469                   methodReturned.callID,
470                   methodReturned.overridden
471                };
472                ack.buffer.WriteData(methodReturned.args, letohd(methodReturned.argsSize));
473                mutex.Wait();
474                object.acks.Add(ack);
475                mutex.Release();
476             }
477             break;
478          }
479       }
480       guiApp.Unlock();
481    }
482
483    void OnDisconnect(int code)
484    {
485       int c;
486       guiApp.Lock();
487       thread.connected = false;
488       guiApp.Unlock();
489       if(thread.started && GetCurrentThreadID() != 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() != 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;
557       DCOMServerSocket socket;
558       thread.connected = false;
559       result = Service::Stop();
560       if(thread.started && GetCurrentThreadID() != thread.id)
561          thread.Wait();
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(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() == 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;
678                unsigned int size = (uint)&((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 = 2;
738    }
739
740    dllexport bool CallMethod(unsigned int methodID)
741    {
742       if(this && connected)
743       {
744          bool result;
745          CallAck ack = null;
746          int callID = nextCallID++;
747          unsigned int size = (uint)&((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() == 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          return result;
785       }
786       return false;
787    }
788
789    ~DCOMClientObject()
790    {
791       if(thread.started)
792       {
793          if(GetCurrentThreadID() != thread.id)
794             thread.Wait();
795       }
796       acks.Free();
797    }
798 };
799
800 // A class to make set of things happen atomically
801 // e.g. to send a bunch of notifications all at once,
802 // without anything else happening in between
803 public class DCOMSendControl
804 {
805    bool sendingOut;
806 public:
807    void Stop()
808    {
809       while(sendingOut) guiApp.Unlock(), ecere::sys::Sleep(0.01), guiApp.Lock();
810       sendingOut = true;
811    }
812
813    void Resume()
814    {
815       sendingOut = false;
816    }
817 }