FreeWRL / FreeX3D 4.3.0
EAI_C_Internals.c
1
2/****************************************************************************
3 This file is part of the FreeWRL/FreeX3D Distribution.
4
5 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
6
7 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
19****************************************************************************/
20
21#include "EAI_C.h"
22#include "config.h"
23#include "system.h"
24
25//#define VERBOSE 1 //JAS
26#define UNUSED(v) ((void) v)
27
28#define WAIT_FOR_RETVAL (command!=SENDEVENT)
29
30static pthread_mutex_t eailock = PTHREAD_MUTEX_INITIALIZER;
31#define EAILOCK pthread_mutex_lock(&eailock);
32#define EAIUNLOCK pthread_mutex_unlock(&eailock);
33
34int _X3D_FreeWRL_FD;
35int _X3D_FreeWRL_Swig_FD = 0;
36int _X3D_FreeWRL_listen_FD = 0;
37int _X3D_queryno = 1;
38
39int receivedData= FALSE;
40/* for waiting on a socket */
41fd_set rfds;
42struct timeval tv;
43struct timeval tv2;
44
45void X3D_error(char *msg) {
46 perror(msg);
47 exit(0);
48}
49
50double mytime;
51
52//char readbuffer[2048];
53char *sendBuffer = NULL;
54int sendBufferSize = 0;
55
56/* handle a callback - this should get a line like:
57 EV
581170697988.125835
5931
600.877656
61EV_EOT
62*/
63
64/* make a buffer large enough to hold our data */
65void verifySendBufferSize (int len) {
66 if (len < (sendBufferSize-50)) return;
67
68 /* make it large enough to contain string, plus some more, as we usually throw some stuff on the beginning. */
69 while (len>(sendBufferSize-200)) sendBufferSize+=1024;
70 sendBuffer = realloc(sendBuffer,sendBufferSize);
71}
72
73/* count the number of numbers on a line - useful for MFNode return value mallocs */
74int _X3D_countWords(char *ptr) {
75 int ct;
76
77 ct = 0;
78
79 while (*ptr >= ' ') {
80 SKIP_CONTROLCHARS
81 SKIP_IF_GT_SPACE
82 ct ++;
83 }
84 return ct;
85}
86#ifdef SWIG
87void *freewrlSwigThread(void* nada) {
88
89 const int on=1;
90 /* unused int flags; */
91 int len;
92
93 struct sockaddr_in servaddr, cliaddr;
94#ifdef WIN32
95 int iResult;
96 WSADATA wsaData;
97
98 iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
99 if (iResult != 0) {
100 X3D_error("WSAStartup failed to load winsock2 ws2_32.dll\n");
101 /* return NULL; */
102 return ;
103 }
104#endif
105
106 if ((_X3D_FreeWRL_listen_FD= socket(AF_INET, SOCK_STREAM, 0)) < 0) {
107 X3D_error("ERROR opening swig socket");
108 /* return NULL; */
109 return ;
110
111 }
112
113 setsockopt(_X3D_FreeWRL_listen_FD, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
114
115 bzero(&servaddr, sizeof(servaddr));
116 servaddr.sin_family = AF_INET;
117 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
118 servaddr.sin_port = htons(EAIBASESOCKET+ 500);
119
120 if (bind((_X3D_FreeWRL_listen_FD), (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
121 X3D_error("ERROR in bind");
122 }
123
124 if (listen(_X3D_FreeWRL_listen_FD, 1024) < 0) {
125 X3D_error("ERROR in listen");
126 /* return NULL; */
127 return ;
128 }
129
130 len = sizeof(cliaddr);
131
132#ifdef WIN32
133 _X3D_FreeWRL_Swig_FD = accept((_X3D_FreeWRL_listen_FD), (struct sockaddr*) &cliaddr, &len);
134#else
135 _X3D_FreeWRL_Swig_FD = accept((_X3D_FreeWRL_listen_FD), (struct sockaddr*) &cliaddr, (socklen_t *) &len);
136#endif
137 /* return NULL; */
138 return ;
139}
140#endif //SWIG
141#include <list.h>
142static s_list_t *evlist = NULL;
143static s_list_t *relist = NULL;
144
145pthread_mutex_t mut_relist = PTHREAD_MUTEX_INITIALIZER;
146pthread_mutex_t mut_evlist = PTHREAD_MUTEX_INITIALIZER;
147pthread_mutex_t mut_re = PTHREAD_MUTEX_INITIALIZER;
148
149pthread_cond_t condition_relist_nonempty = PTHREAD_COND_INITIALIZER;
150pthread_cond_t condition_evlist_nonempty = PTHREAD_COND_INITIALIZER;
151pthread_cond_t condition_re_done = PTHREAD_COND_INITIALIZER;
152
153
154s_list_t* _enqueue_readbuffer(s_list_t *list, char *readbuffer){
155 s_list_t* item;
156 char *cbline = strdup(readbuffer);
157 item = ml_new(cbline);
158 list = ml_append(list, item);
159 return list;
160}
161void _enqueue_readbuffer_re(char *readbuffer)
162{
163 pthread_mutex_lock (&mut_relist);
164 relist = _enqueue_readbuffer(relist,readbuffer);
165 pthread_cond_signal(&condition_relist_nonempty);
166 pthread_mutex_unlock (&mut_relist);
167}
168void _enqueue_readbuffer_ev(char *readbuffer)
169{
170 pthread_mutex_lock (&mut_evlist);
171 evlist = _enqueue_readbuffer(evlist,readbuffer);
172 pthread_cond_signal(&condition_evlist_nonempty);
173 pthread_mutex_unlock (&mut_evlist);
174}
175char* dequeue_readbuffer(s_list_t **plist)
176{
177 s_list_t *list = *plist;
178 if(list){
179 char *readbuffer = ml_elem(list);
180 *plist = ml_delete_self(list, list);
181 return readbuffer;
182 }else
183 return NULL;
184}
185int waiting_for_RE = 0;
186char *dequeue_readbuffer_wait_re()
187{
188 char *readbuffer;
189 pthread_mutex_lock (&mut_re); //hold up ev work
190 waiting_for_RE = 1; //set flag for holding up ev
191
192 pthread_mutex_lock (&mut_relist);
193 if(relist == NULL)
194 pthread_cond_wait(&condition_relist_nonempty, &mut_relist);
195 readbuffer = dequeue_readbuffer(&relist);
196 pthread_mutex_unlock (&mut_relist);
197
198 waiting_for_RE = 0;
199 pthread_cond_signal(&condition_re_done); //signal ev OK
200 pthread_mutex_unlock (&mut_re); //release ev work
201 return readbuffer;
202}
203char* dequeue_readbuffer_ev(int wait)
204{
205 char *readbuffer;
206 pthread_mutex_lock (&mut_evlist);
207 if(evlist == NULL){
208 if(!wait){
209 pthread_mutex_unlock (&mut_evlist);
210 return NULL;
211 }
212 pthread_cond_wait(&condition_evlist_nonempty, &mut_evlist);
213 }
214 readbuffer = dequeue_readbuffer(&evlist);
215 pthread_mutex_unlock (&mut_evlist);
216 return readbuffer;
217}
218/* Dave says:
219 "If you get an EV while still waiting for any REs you should not
220 handle the EV until the RE queue is empty and you are not expecting
221 any REs."
222 In sendToFreewrl we send, then immediately set waiting_for_RE if/while
223 we are waiting, and here we skip/return if that's the case.
224*/
225void dequeue_callback_ev(int wait)
226{
227 char* cbline;
228 if(!wait)
229 {
230 if(waiting_for_RE) return;
231 cbline = dequeue_readbuffer_ev(wait);
232 if(cbline){
233 _handleFreeWRLcallback (cbline) ;
234 free(cbline);
235 }
236 }else if(wait) {
237 cbline = dequeue_readbuffer_ev(wait);
238 if(cbline){
239 pthread_mutex_lock (&mut_re);
240 if(waiting_for_RE)
241 pthread_cond_wait(&condition_re_done, &mut_re);
242 pthread_mutex_unlock (&mut_re);
243 _handleFreeWRLcallback (cbline) ;
244 free(cbline);
245 }
246 }
247}
248/* you'd start this thread if you have no main.c to call dequeue_callback_ev() from,
249 such as SAI in javascript.
250*/
251void *freewrlEVcallbackThread(void* nada) {
252 while(1){
253 dequeue_callback_ev(1);
254 }
255 return nada;
256}
257
258/* read in the reply - if it is an RE; it is the reply to an event; if it is an
259 EV it is an async event */
260void *freewrlReadThread(void* nada) {
261 int retval;
262 char readbuffer[2048];
263 //initialize_queue_mutexes();
264 while (1==1) {
265
266
267 tv.tv_sec = 0;
268 tv.tv_usec = 100;
269 FD_ZERO(&rfds);
270 FD_SET(_X3D_FreeWRL_FD, &rfds);
271
272 /* wait for the socket. We HAVE to select on "sock+1" - RTFM */
273 /* WIN32 ignors the first select parameter, just there for berkley compatibility */
274 // retval = select(_X3D_FreeWRL_FD+1, &rfds, NULL, NULL, &tv); //times out
275 retval = select(_X3D_FreeWRL_FD+1, &rfds, NULL, NULL, NULL); //blocking
276
277#ifdef VERBOSE
278 if(retval)printf("+");
279 else printf("-");
280#endif
281
282 if (retval) {
283#ifdef WIN32
284 retval = recv(_X3D_FreeWRL_FD, readbuffer, 2048, 0);
285 if (retval == SOCKET_ERROR) {
286#else
287 retval = read(_X3D_FreeWRL_FD,readbuffer,2048);
288 if (retval <= 0) {
289#endif
290 printf("ERROR reading fromsocket\n");
291 exit(1);
292 }
293 readbuffer[retval] = '\0';
294
295 /* if this is normal data - signal that it is received */
296 if (strncmp ("RE",readbuffer,2) == 0) {
297 if(0)
298 receivedData = TRUE;
299 if(1)
300 _enqueue_readbuffer_re(readbuffer);
301 } else if (strncmp ("EV",readbuffer,2) == 0) {
302 if(0)
303 _handleFreeWRLcallback(readbuffer);
304 if(1)
305 _enqueue_readbuffer_ev(readbuffer);
306 } else if (strncmp ("QUIT",readbuffer,4) == 0) {
307 exit(0);
308 } else {
309 printf ("readThread - unknown prefix - %s\n",readbuffer);
310 }
311
312 }
313
314 }
315 return nada;
316}
317
318/* threading - we thread only to read from a different thread. This
319allows events and so on to go quickly - no return value required. */
320char readbuffer[2048];
321
322static char *sendToFreeWRL(char *callerbuffer, int size, int waitForResponse) {
323 int retval;
324 int readquery;
325 char *ptr;
326
327 #ifdef VERBOSE
328 printf ("sendToFreeWRL - sending :%s:\n",callerbuffer);
329 #endif
330
331#ifdef WIN32
332 ptr = NULL;
333 receivedData = FALSE;
334 retval = send(_X3D_FreeWRL_FD, callerbuffer, size, 0);
335 if (retval == SOCKET_ERROR )
336#else
337 ptr = NULL;
338 retval = write(_X3D_FreeWRL_FD, callerbuffer, size);
339 #ifdef VERBOSE
340 printf ("sendToFreeWRL, sent callbuffer %s of size %d, retval %d\n",callerbuffer,size,retval);
341 #endif
342
343 if (retval < 0)
344#endif
345 X3D_error("ERROR writing to socket");
346
347 if (waitForResponse) {
348
349 //receivedData = FALSE;
350 if(0)
351 while (!receivedData) {
352 sched_yield();
353 }
354 if(1){
355 char *rb;
356 //sched_yield();
357 rb = dequeue_readbuffer_wait_re();
358 strcpy(readbuffer,rb);
359 free(rb);
360 }
361
362
363 /* have the response here now. */
364
365 #ifdef VERBOSE
366 printf("Client got: %s\n",readbuffer);
367 #endif
368
369 /* should return something like: RE
3701165347857.925786
3711
3720.000000
373RE_EOT
374*/
375 /* see if it is a reply, or an event return */
376
377
378 ptr = readbuffer;
379 while ((*ptr != '\0') && (*ptr <= ' ')) ptr++;
380
381 #ifdef VERBOSE
382 printf ("found a reply\n");
383 #endif
384
385 SKIP_IF_GT_SPACE
386 SKIP_CONTROLCHARS
387 if (sscanf(ptr,"%lf",&mytime) != 1) {
388 printf ("huh, expected the time, got %s\n",ptr);
389 exit(1);
390 }
391 #ifdef VERBOSE
392 printf ("time of command is %lf\n",mytime);
393 #endif
394
395 SKIP_IF_GT_SPACE
396 SKIP_CONTROLCHARS
397
398 #ifdef VERBOSE
399 printf ("this should be the query number: %s\n",ptr);
400 #endif
401
402 if (sscanf(ptr,"%d",&readquery) != 1) {
403 printf ("huh, expected the time, got %s\n",ptr);
404 exit(1);
405 }
406 #ifdef VERBOSE
407 printf ("query returned is %d\n",readquery);
408 #endif
409
410 if (_X3D_queryno != readquery) {
411 printf ("server: warning, _X3D_queryno %d != received %d\n",_X3D_queryno,readquery);
412 usleep(5000);
413 sched_yield();
414 }
415
416 SKIP_IF_GT_SPACE
417 SKIP_CONTROLCHARS
418
419
420 strncpy(callerbuffer,readbuffer,retval);
421
422 }
423 _X3D_queryno ++;
424 #ifdef VERBOSE
425 printf ("sendToFreeWRL, returning %p\n",ptr);
426 #endif
427
428
429 return ptr;
430}
431
432void _X3D_sendEvent (char command, char *string,int line) {
433 char *myptr;
434 UNUSED (myptr); // mitigate compiler warnings
435 EAILOCK
436 verifySendBufferSize (strlen(string));
437 //JAS printf ("_X3D_sendEvent, sending string %s, called from line %d\n",string,line);
438
439 sprintf (sendBuffer, "%d %c %s\n",_X3D_queryno,command,string);
440 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),WAIT_FOR_RETVAL);
441 EAIUNLOCK
442 //JAS printf ("_X3D_sendEvent, sent, returning...\n");
443}
444
445char *_X3D_makeShortCommand (char command) {
446 char *myptr;
447
448 EAILOCK
449 verifySendBufferSize (100);
450 sprintf (sendBuffer, "%d %c\n",_X3D_queryno,command);
451 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),WAIT_FOR_RETVAL);
452 EAIUNLOCK
453 #ifdef VERBOSE
454 printf ("makeShortCommand, buffer now %s\n",myptr);
455 #endif
456 return myptr;
457}
458
459char *_X3D_make1VoidCommand (char command, int adr) {
460 char *myptr;
461
462 EAILOCK
463 verifySendBufferSize (100);
464 sprintf (sendBuffer, "%d %c %d\n",_X3D_queryno,command,adr);
465 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),WAIT_FOR_RETVAL);
466 EAIUNLOCK
467 #ifdef VERBOSE
468 printf ("make1VoidCommand, buffer now %s\n",myptr);
469 #endif
470 return myptr;
471}
472
473char *_X3D_make1StringCommand (char command, char *name) {
474 char *myptr;
475
476 #ifdef VERBOSE
477 printf ("start _X3D_make1StringCommand, command %c char %s\n",command,name);
478 #endif
479
480 EAILOCK
481 verifySendBufferSize (strlen(name));
482 sprintf (sendBuffer, "%d %c %s\n",_X3D_queryno,command,name);
483 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),WAIT_FOR_RETVAL);
484 EAIUNLOCK
485
486 #ifdef VERBOSE
487 printf ("make1StringCommand, buffer now %s\n",myptr);
488 #endif
489
490 return myptr;
491}
492
493
494char *_X3D_make2StringCommand (char command, char *str1, char *str2) {
495 char *myptr;
496 char sendBuffer[2048];
497
498 EAILOCK
499 verifySendBufferSize ( strlen(str1) + strlen(str2));
500 sprintf (sendBuffer, "%d %c %s%s\n",_X3D_queryno,command,str1,str2);
501 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),WAIT_FOR_RETVAL);
502 EAIUNLOCK
503
504 #ifdef VERBOSE
505 printf ("make2StringCommand, buffer now %s\n",myptr);
506 #endif
507 return myptr;
508}
509
510
511char *_X3D_Browser_SendEventType(int adr,char *name, char *evtype) {
512 char *myptr;
513
514 EAILOCK
515 verifySendBufferSize (100);
516 sprintf (sendBuffer, "%d %c %d %s %s\n",_X3D_queryno, GETFIELDTYPE, adr, name, evtype);
517
518 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),TRUE);
519 EAIUNLOCK
520 #ifdef VERBOSE
521 printf ("_X3D_Browser_SendEventType, buffer now %s\n",myptr);
522 #endif
523 return myptr;
524}
525
526char * _RegisterListener (X3DEventOut *node, int adin) {
527 char *myptr;
528
529
530 verifySendBufferSize (100);
531 #ifdef VERBOSE
532 printf ("in RegisterListener, we have query %d advise index %d nodeptr %d offset %d datatype %d datasize %d field %s\n",
533 _X3D_queryno,
534 adin, node->nodeptr, node->offset, node->datatype, node->datasize, node->field);
535 #endif
536
537/*
538 EAIoutSender.send ("" + queryno + "G " + nodeptr + " " + offset + " " + datatype +
539 " " + datasize + "\n");
540*/
541 EAILOCK
542 sprintf (sendBuffer, "%u %c %d %d %c %d\n",
543 _X3D_queryno,
544 REGLISTENER,
545 node->nodeptr,
546 node->offset,
547 mapFieldTypeToEAItype(node->datatype),
548 node->datasize);
549
550 myptr = sendToFreeWRL(sendBuffer, strlen(sendBuffer),TRUE);
551 EAIUNLOCK
552 #ifdef VERBOSE
553 printf ("_X3D_Browser_SendEventType, buffer now %s\n",myptr);
554 #endif
555 return myptr;
556}
557