|
|
1.1 root 1: /* js_queue.c */
2:
3: /* Synchronet JavaScript "Queue" Object */
4:
1.1.1.2 ! root 5: /* $Id: js_queue.c,v 1.24 2011/09/08 07:10:26 rswindell Exp $ */
1.1 root 6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
1.1.1.2 ! root 11: * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html *
1.1 root 12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39: #include "msg_queue.h"
1.1.1.2 ! root 40: #include "js_request.h"
1.1 root 41:
42: typedef struct
43: {
44: char name[128];
45: int type;
46: union {
47: JSBool b;
48: jsdouble n;
49: char* s;
50: } value;
51: } queued_value_t;
52:
53: link_list_t named_queues;
54:
55: static const char* getprivate_failure = "line %d %s JS_GetPrivate failed";
56:
57: /* Queue Destructor */
58:
59: static void js_finalize_queue(JSContext *cx, JSObject *obj)
60: {
61: msg_queue_t* q;
62: list_node_t* n;
63:
64: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL)
65: return;
1.1.1.2 ! root 66:
1.1 root 67: if(msgQueueDetach(q)==0 && (n=listFindNode(&named_queues,q,/* length=0 for ptr compare */0))!=NULL)
68: listRemoveNode(&named_queues,n,FALSE);
69:
70: JS_SetPrivate(cx, obj, NULL);
71: }
72:
73: static size_t js_decode_value(JSContext *cx, JSObject *parent
74: ,queued_value_t* v, jsval* rval, BOOL peek)
75: {
76: size_t count=1;
77: size_t decoded;
78: queued_value_t* pv;
79: queued_value_t term;
80: jsval prop_val;
81: jsuint index=0;
82: JSObject *obj;
83:
84: ZERO_VAR(term);
85:
86: *rval = JSVAL_VOID;
87:
88: if(v==NULL || v->type==JSTYPE_VOID)
89: return(count);
90:
91: switch(v->type) {
92: case JSTYPE_NULL:
93: *rval = JSVAL_NULL;
94: break;
95: case JSTYPE_BOOLEAN:
96: *rval = BOOLEAN_TO_JSVAL(v->value.b);
97: break;
98: case JSTYPE_NUMBER:
99: JS_NewNumberValue(cx,v->value.n,rval);
100: break;
101: case JSTYPE_STRING:
102: if(v->value.s) {
103: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,v->value.s));
104: if(!peek)
105: free(v->value.s);
106: }
107: break;
108: case JSTYPE_ARRAY:
109: case JSTYPE_OBJECT:
110: obj = JS_DefineObject(cx, parent, v->name, NULL, NULL
111: ,JSPROP_ENUMERATE);
112: for(pv=v+1,count++;memcmp(pv,&term,sizeof(term));pv+=decoded,count+=decoded) {
113: decoded=js_decode_value(cx,obj,pv,&prop_val,peek);
114: if(v->type==JSTYPE_ARRAY)
115: JS_SetElement(cx,obj,index++,&prop_val);
116: else
117: JS_DefineProperty(cx, obj, pv->name, prop_val,NULL,NULL,JSPROP_ENUMERATE);
118: }
119: *rval = OBJECT_TO_JSVAL(obj);
120: break;
121: }
122: return(count);
123: }
124:
125: /* Queue Object Methods */
126:
127: static JSBool
128: js_poll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
129: {
130: msg_queue_t* q;
131: queued_value_t* v;
132: int32 timeout=0;
1.1.1.2 ! root 133: jsrefcount rc;
1.1 root 134:
135: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
136: JS_ReportError(cx,getprivate_failure,WHERE);
137: return(JS_FALSE);
138: }
139:
140: if(argc && JSVAL_IS_NUMBER(argv[0])) /* timeout specified */
141: JS_ValueToInt32(cx,argv[0],&timeout);
142:
1.1.1.2 ! root 143: rc=JS_SUSPENDREQUEST(cx);
! 144: v=msgQueuePeek(q,timeout);
! 145: JS_RESUMEREQUEST(cx, rc);
! 146: if(v==NULL)
1.1 root 147: *rval = JSVAL_FALSE;
148: else if(v->name!=NULL && v->name[0])
149: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,v->name));
150: else
151: *rval = JSVAL_TRUE;
152:
153: return(JS_TRUE);
154: }
155:
156: static JSBool
157: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
158: {
159: msg_queue_t* q;
160: queued_value_t find_v;
161: queued_value_t* v;
162: int32 timeout=0;
1.1.1.2 ! root 163: jsrefcount rc;
1.1 root 164:
165: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
166: JS_ReportError(cx,getprivate_failure,WHERE);
167: return(JS_FALSE);
168: }
169:
170: if(JSVAL_IS_STRING(argv[0])) { /* value named specified */
171: ZERO_VAR(find_v);
172: SAFECOPY(find_v.name,JS_GetStringBytes(JS_ValueToString(cx,argv[0])));
1.1.1.2 ! root 173: rc=JS_SUSPENDREQUEST(cx);
1.1 root 174: v=msgQueueFind(q,&find_v,sizeof(find_v.name));
1.1.1.2 ! root 175: JS_RESUMEREQUEST(cx, rc);
1.1 root 176: } else {
177: if(JSVAL_IS_NUMBER(argv[0]))
178: JS_ValueToInt32(cx,argv[0],&timeout);
1.1.1.2 ! root 179: rc=JS_SUSPENDREQUEST(cx);
1.1 root 180: v=msgQueueRead(q, timeout);
1.1.1.2 ! root 181: JS_RESUMEREQUEST(cx, rc);
1.1 root 182: }
183:
184: if(v!=NULL) {
185: js_decode_value(cx, obj, v, rval, /* peek */FALSE);
186: free(v);
187: }
188:
189: return(JS_TRUE);
190: }
191:
192: static JSBool
193: js_peek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
194: {
195: msg_queue_t* q;
196: queued_value_t* v;
197: int32 timeout=0;
1.1.1.2 ! root 198: jsrefcount rc;
1.1 root 199:
200: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
201: JS_ReportError(cx,getprivate_failure,WHERE);
202: return(JS_FALSE);
203: }
204:
205: if(argc && JSVAL_IS_NUMBER(argv[0])) /* timeout specified */
206: JS_ValueToInt32(cx,argv[0],&timeout);
207:
1.1.1.2 ! root 208: rc=JS_SUSPENDREQUEST(cx);
! 209: v=msgQueuePeek(q, timeout);
! 210: JS_RESUMEREQUEST(cx, rc);
! 211: if(v!=NULL) {
1.1 root 212: js_decode_value(cx, obj, v, rval, /* peek */TRUE);
213: }
214:
215: return(JS_TRUE);
216: }
217:
218: static queued_value_t* js_encode_value(JSContext *cx, jsval val, char* name
219: ,queued_value_t* v, size_t* count)
220: {
221: jsint i;
222: jsval prop_name;
223: jsval prop_val;
224: JSObject* obj;
225: JSIdArray* id_array;
226: queued_value_t* nv;
227:
228: if((nv=realloc(v,((*count)+1)*sizeof(queued_value_t)))==NULL) {
229: if(v) free(v);
230: return(NULL);
231: }
232: v=nv;
233: nv=v+(*count);
234: memset(nv,0,sizeof(queued_value_t));
235: (*count)++;
236:
237: if(name!=NULL)
238: SAFECOPY(nv->name,name);
239:
240: switch(JSVAL_TAG(val)) {
241: case JSVAL_BOOLEAN:
242: nv->type=JSTYPE_BOOLEAN;
243: nv->value.b=JSVAL_TO_BOOLEAN(val);
244: break;
245: case JSVAL_OBJECT:
246: if(JSVAL_IS_NULL(val)) {
247: nv->type=JSTYPE_NULL;
248: break;
249: }
250: nv->type=JSTYPE_OBJECT;
251: obj = JSVAL_TO_OBJECT(val);
252:
253: if(JS_IsArrayObject(cx, obj))
254: nv->type=JSTYPE_ARRAY;
255:
256: if((id_array=JS_Enumerate(cx,obj))==NULL) {
257: free(v);
258: return(NULL);
259: }
260: for(i=0; i<id_array->length; i++) {
261: /* property name */
262: JS_IdToValue(cx,id_array->vector[i],&prop_name);
263: if(JSVAL_IS_STRING(prop_name)) {
264: name=JS_GetStringBytes(JSVAL_TO_STRING(prop_name));
265: /* value */
266: JS_GetProperty(cx,obj,name,&prop_val);
267: } else {
268: name=NULL;
269: JS_GetElement(cx,obj,i,&prop_val);
270: }
271: if((v=js_encode_value(cx,prop_val,name,v,count))==NULL)
272: break;
273: }
274: v=js_encode_value(cx,JSVAL_VOID,NULL,v,count); /* terminate object */
275: JS_DestroyIdArray(cx,id_array);
276: break;
277: default:
278: if(JSVAL_IS_NUMBER(val)) {
279: nv->type = JSTYPE_NUMBER;
280: JS_ValueToNumber(cx,val,&nv->value.n);
281: } else if(JSVAL_IS_VOID(val)) {
282: nv->type = JSTYPE_VOID;
283: } else {
284: nv->type= JSTYPE_STRING;
285: nv->value.s = strdup(JS_GetStringBytes(JS_ValueToString(cx,val)));
286: }
287: break;
288: }
289:
290: return(v);
291: }
292:
293: BOOL js_enqueue_value(JSContext *cx, msg_queue_t* q, jsval val, char* name)
294: {
295: queued_value_t* v;
296: size_t count=0;
297: BOOL result;
1.1.1.2 ! root 298: jsrefcount rc;
1.1 root 299:
300: if((v=js_encode_value(cx,val,name,NULL,&count))==NULL || count<1)
301: return(FALSE);
302:
1.1.1.2 ! root 303: rc=JS_SUSPENDREQUEST(cx);
1.1 root 304: result=msgQueueWrite(q,v,count*sizeof(queued_value_t));
305: free(v);
1.1.1.2 ! root 306: JS_RESUMEREQUEST(cx, rc);
1.1 root 307: return(result);
308: }
309:
310: static JSBool
311: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
312: {
313: uintN argn=0;
314: msg_queue_t* q;
315: jsval val;
316: char* name=NULL;
317:
318: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
319: JS_ReportError(cx,getprivate_failure,WHERE);
320: return(JS_FALSE);
321: }
322:
323: val = argv[argn++];
324:
325: if(argn < argc)
326: name=JS_GetStringBytes(JS_ValueToString(cx,argv[argn++]));
327:
328: *rval = BOOLEAN_TO_JSVAL(js_enqueue_value(cx, q, val, name));
329:
330: return(JS_TRUE);
331: }
332:
333: /* Queue Object Properites */
334: enum {
335: QUEUE_PROP_NAME
336: ,QUEUE_PROP_DATA_WAITING
337: ,QUEUE_PROP_READ_LEVEL
338: ,QUEUE_PROP_WRITE_LEVEL
339: };
340:
341: #ifdef BUILD_JSDOCS
342: static char* queue_prop_desc[] = {
343: "name of the queue (if it has one)"
344: ,"<i>true</i> if data is waiting to be read from queue"
345: ,"number of values in the read queue"
346: ,"number of values in the write qeueue"
347: ,NULL
348: };
349: #endif
350:
351: static JSBool js_queue_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
352: {
353: jsint tiny;
354: msg_queue_t* q;
1.1.1.2 ! root 355: jsrefcount rc;
1.1 root 356:
357: if((q=(msg_queue_t*)JS_GetPrivate(cx,obj))==NULL) {
358: JS_ReportError(cx,getprivate_failure,WHERE);
359: return(JS_FALSE);
360: }
361:
362: tiny = JSVAL_TO_INT(id);
363:
364: switch(tiny) {
365: case QUEUE_PROP_NAME:
366: if(q->name!=NULL && q->name[0])
367: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,q->name));
368: break;
369: case QUEUE_PROP_DATA_WAITING:
1.1.1.2 ! root 370: rc=JS_SUSPENDREQUEST(cx);
1.1 root 371: *vp = BOOLEAN_TO_JSVAL(INT_TO_BOOL(msgQueueReadLevel(q)));
1.1.1.2 ! root 372: JS_RESUMEREQUEST(cx, rc);
1.1 root 373: break;
374: case QUEUE_PROP_READ_LEVEL:
1.1.1.2 ! root 375: rc=JS_SUSPENDREQUEST(cx);
1.1 root 376: *vp = INT_TO_JSVAL(msgQueueReadLevel(q));
1.1.1.2 ! root 377: JS_RESUMEREQUEST(cx, rc);
1.1 root 378: break;
379: case QUEUE_PROP_WRITE_LEVEL:
1.1.1.2 ! root 380: rc=JS_SUSPENDREQUEST(cx);
1.1 root 381: *vp = INT_TO_JSVAL(msgQueueWriteLevel(q));
1.1.1.2 ! root 382: JS_RESUMEREQUEST(cx, rc);
1.1 root 383: break;
384: }
385: return(JS_TRUE);
386: }
387:
388: #define QUEUE_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
389:
390: static jsSyncPropertySpec js_queue_properties[] = {
391: /* name ,tinyid ,flags, ver */
392:
393: { "name" ,QUEUE_PROP_NAME ,QUEUE_PROP_FLAGS, 312 },
394: { "data_waiting" ,QUEUE_PROP_DATA_WAITING,QUEUE_PROP_FLAGS, 312 },
395: { "read_level" ,QUEUE_PROP_READ_LEVEL ,QUEUE_PROP_FLAGS, 312 },
396: { "write_level" ,QUEUE_PROP_WRITE_LEVEL ,QUEUE_PROP_FLAGS, 312 },
397: {0}
398: };
399:
400: static jsSyncMethodSpec js_queue_functions[] = {
401: {"poll", js_poll, 1, JSTYPE_UNDEF, "[timeout=<tt>0</tt>]"
402: ,JSDOCSTR("wait for any value to be written to the queue for up to <i>timeout</i> milliseconds "
403: "(default: <i>0</i>), returns <i>true</i> or the <i>name</i> (string) of "
404: "the value waiting (if it has one), or <i>false</i> if no values are waiting")
405: ,312
406: },
407: {"read", js_read, 1, JSTYPE_UNDEF, "[string name] or [timeout=<tt>0</tt>]"
408: ,JSDOCSTR("read a value from the queue, if <i>name</i> not specified, reads next value "
409: "from the bottom of the queue (waiting up to <i>timeout</i> milliseconds)")
410: ,313
411: },
412: {"peek", js_peek, 1, JSTYPE_UNDEF, "[timeout=<tt>0</tt>]"
413: ,JSDOCSTR("peek at the value at the bottom of the queue, "
414: "wait up to <i>timeout</i> milliseconds for any value to be written "
415: "(default: <i>0</i>)")
416: ,313
417: },
418: {"write", js_write, 1, JSTYPE_BOOLEAN, "value [,name=<i>none</i>]"
419: ,JSDOCSTR("write a value (optionally named) to the queue")
420: ,312
421: },
422: {0}
423: };
424:
1.1.1.2 ! root 425: static JSBool js_queue_resolve(JSContext *cx, JSObject *obj, jsval id)
! 426: {
! 427: char* name=NULL;
! 428:
! 429: if(id != JSVAL_NULL)
! 430: name=JS_GetStringBytes(JSVAL_TO_STRING(id));
! 431:
! 432: return(js_SyncResolve(cx, obj, name, js_queue_properties, js_queue_functions, NULL, 0));
! 433: }
! 434:
! 435: static JSBool js_queue_enumerate(JSContext *cx, JSObject *obj)
! 436: {
! 437: return(js_queue_resolve(cx, obj, JSVAL_NULL));
! 438: }
! 439:
! 440: static JSClass js_queue_class = {
! 441: "Queue" /* name */
! 442: ,JSCLASS_HAS_PRIVATE /* flags */
! 443: ,JS_PropertyStub /* addProperty */
! 444: ,JS_PropertyStub /* delProperty */
! 445: ,js_queue_get /* getProperty */
! 446: ,JS_PropertyStub /* setProperty */
! 447: ,js_queue_enumerate /* enumerate */
! 448: ,js_queue_resolve /* resolve */
! 449: ,JS_ConvertStub /* convert */
! 450: ,js_finalize_queue /* finalize */
! 451: };
! 452:
1.1 root 453: /* Queue Constructor (creates queue) */
454:
455: static JSBool
456: js_queue_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
457: {
458: uintN argn=0;
459: char* name=NULL;
460: int32 flags=MSG_QUEUE_BIDIR;
461: msg_queue_t* q=NULL;
462: list_node_t* n;
1.1.1.2 ! root 463: jsrefcount rc;
1.1 root 464:
465: *rval = JSVAL_VOID;
466:
467: #if 0 /* This doesn't appear to be doing anything but leaking memory */
468: if((q=(msg_queue_t*)malloc(sizeof(msg_queue_t)))==NULL) {
469: JS_ReportError(cx,"malloc failed");
470: return(JS_FALSE);
471: }
472: memset(q,0,sizeof(msg_queue_t));
473: #endif
474:
475: if(argn<argc && JSVAL_IS_STRING(argv[argn]))
476: name=JS_GetStringBytes(JS_ValueToString(cx,argv[argn++]));
477:
478: if(argn<argc && JSVAL_IS_NUMBER(argv[argn]))
479: JS_ValueToInt32(cx,argv[argn++],&flags);
480:
1.1.1.2 ! root 481: rc=JS_SUSPENDREQUEST(cx);
1.1 root 482: if(name!=NULL) {
1.1.1.2 ! root 483: listLock(&named_queues);
! 484: for(n=named_queues.first;n!=NULL;n=n->next)
1.1 root 485: if((q=n->data)!=NULL && !stricmp(q->name,name))
486: break;
1.1.1.2 ! root 487: listUnlock(&named_queues);
1.1 root 488: if(n==NULL)
489: q=NULL;
490: }
491:
492: if(q==NULL) {
493: q=msgQueueInit(NULL,flags);
494: if(name!=NULL)
495: SAFECOPY(q->name,name);
496: listPushNode(&named_queues,q);
497: } else
498: msgQueueAttach(q);
1.1.1.2 ! root 499: JS_RESUMEREQUEST(cx, rc);
1.1 root 500:
501: if(!JS_SetPrivate(cx, obj, q)) {
502: JS_ReportError(cx,"JS_SetPrivate failed");
503: return(JS_FALSE);
504: }
505:
506: #ifdef BUILD_JSDOCS
507: js_DescribeSyncObject(cx,obj,"Class for bi-directional message queues. "
508: "Used for inter-thread/module communications.", 312);
509: js_DescribeSyncConstructor(cx,obj,"To create a new (named) Queue object: "
510: "<tt>var q = new Queue(<i>name</i>)</tt>");
511: js_CreateArrayOfStrings(cx, obj, "_property_desc_list", queue_prop_desc, JSPROP_READONLY);
512: #endif
513:
514: return(JS_TRUE);
515: }
516:
517: JSObject* DLLCALL js_CreateQueueClass(JSContext* cx, JSObject* parent)
518: {
519: JSObject* obj;
520:
521: obj = JS_InitClass(cx, parent, NULL
522: ,&js_queue_class
523: ,js_queue_constructor
524: ,0 /* number of constructor args */
525: ,NULL /* props, specified in constructor */
526: ,NULL /* funcs, specified in constructor */
527: ,NULL
528: ,NULL);
529:
530: return(obj);
531: }
532:
533: JSObject* DLLCALL js_CreateQueueObject(JSContext* cx, JSObject* parent, char *name, msg_queue_t* q)
534: {
535: JSObject* obj;
536:
537: if(name==NULL)
538: obj = JS_NewObject(cx, &js_queue_class, NULL, parent);
539: else
540: obj = JS_DefineObject(cx, parent, name, &js_queue_class, NULL
541: ,JSPROP_ENUMERATE|JSPROP_READONLY);
542:
543: if(obj==NULL)
544: return(NULL);
545:
546: if(!JS_SetPrivate(cx, obj, q))
547: return(NULL);
548:
549: return(obj);
550: }
551:
552:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.