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