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