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