/* * 'ajduk' specific functionality, examples for low memory techniques */ #ifdef DUK_CMDLINE_AJSHEAP #include #include #include #include #include "ajs.h" #include "ajs_heap.h" #include "duktape.h" extern uint8_t dbgHEAPDUMP; #if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) /* Pointer compression with ROM strings/objects: * * For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM * pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer * compression range minimum to avoid duplication in user code. */ #if 0 /* This extern declaration is provided by duktape.h, array provided by duktape.c. */ extern const void * const duk_rom_compressed_pointers[]; #endif static const void *duk__romptr_low = NULL; static const void *duk__romptr_high = NULL; #define DUK__ROMPTR_COMPRESSION #define DUK__ROMPTR_FIRST DUK_USE_ROM_PTRCOMP_FIRST #endif /* * Helpers */ static void *ajduk__lose_const(const void *ptr) { /* Somewhat portable way of losing a const without warnings. * Another approach is to cast through intptr_t, but that * type is not always available. */ union { const void *p; void *q; } u; u.p = ptr; return u.q; } static void safe_print_chars(const char *p, duk_size_t len, int until_nul) { duk_size_t i; printf("\""); for (i = 0; i < len; i++) { unsigned char x = (unsigned char) p[i]; if (until_nul && x == 0U) { break; } if (x < 0x20 || x >= 0x7e || x == '"' || x == '\'' || x == '\\') { printf("\\x%02x", (int) x); } else { printf("%c", (char) x); } } printf("\""); } /* * Heap initialization when using AllJoyn.js pool allocator (without any * other AllJoyn.js integration). This serves as an example of how to * integrate Duktape with a pool allocator and is useful for low memory * testing. * * The pool sizes are not optimized here. The sizes are chosen so that * you can look at the high water mark (hwm) and use counts (use) and see * how much allocations are needed for each pool size. To optimize pool * sizes more accurately, you can use --alloc-logging and inspect the memory * allocation log which provides exact byte counts etc. * * https://git.allseenalliance.org/cgit/core/alljoyn-js.git * https://git.allseenalliance.org/cgit/core/alljoyn-js.git/tree/ajs.c */ static const AJS_HeapConfig ajsheap_config[] = { { 8, 10, AJS_POOL_BORROW, 0 }, { 12, 600, AJS_POOL_BORROW, 0 }, { 16, 300, AJS_POOL_BORROW, 0 }, { 20, 300, AJS_POOL_BORROW, 0 }, { 24, 300, AJS_POOL_BORROW, 0 }, { 28, 150, AJS_POOL_BORROW, 0 }, { 32, 150, AJS_POOL_BORROW, 0 }, { 40, 150, AJS_POOL_BORROW, 0 }, { 48, 50, AJS_POOL_BORROW, 0 }, { 52, 50, AJS_POOL_BORROW, 0 }, { 56, 50, AJS_POOL_BORROW, 0 }, { 60, 50, AJS_POOL_BORROW, 0 }, { 64, 50, AJS_POOL_BORROW, 0 }, { 128, 80, AJS_POOL_BORROW, 0 }, { 256, 16, AJS_POOL_BORROW, 0 }, { 320, 1, AJS_POOL_BORROW, 0 }, { 392, 1, AJS_POOL_BORROW, 0 }, /* duk_hthread, with heap ptr compression, ROM strings+objects */ { 512, 16, AJS_POOL_BORROW, 0 }, { 964, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression, ROM strings+objects */ { 1024, 6, AJS_POOL_BORROW, 0 }, { 1344, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression, RAM strings+objects */ { 2048, 5, AJS_POOL_BORROW, 0 }, { 4096, 3, 0, 0 }, { 8192, 3, 0, 0 }, { 16384, 1, 0, 0 }, { 32768, 1, 0, 0 } }; uint8_t *ajsheap_ram = NULL; void ajsheap_init(void) { size_t heap_sz[1]; uint8_t *heap_array[1]; uint8_t num_pools, i; AJ_Status ret; num_pools = (uint8_t) (sizeof(ajsheap_config) / sizeof(AJS_HeapConfig)); heap_sz[0] = AJS_HeapRequired(ajsheap_config, /* heapConfig */ num_pools, /* numPools */ 0); /* heapNum */ ajsheap_ram = (uint8_t *) malloc(heap_sz[0]); if (ajsheap_ram == NULL) { fprintf(stderr, "Failed to allocate AJS heap\n"); fflush(stderr); exit(1); } heap_array[0] = ajsheap_ram; fprintf(stderr, "Allocated AJS heap of %ld bytes, pools:", (long) heap_sz[0]); for (i = 0; i < num_pools; i++) { fprintf(stderr, " (sz:%ld,num:%ld,brw:%ld,idx:%ld)", (long) ajsheap_config[i].size, (long) ajsheap_config[i].entries, (long) ajsheap_config[i].borrow, (long) ajsheap_config[i].heapIndex); } fprintf(stderr, "\n"); fflush(stderr); ret = AJS_HeapInit((void **) heap_array, /* heap */ (size_t *) heap_sz, /* heapSz */ ajsheap_config, /* heapConfig */ num_pools, /* numPools */ 1); /* numHeaps */ fprintf(stderr, "AJS_HeapInit() -> %ld\n", (long) ret); fflush(stderr); /* Enable heap dumps */ dbgHEAPDUMP = 1; #if defined(DUK__ROMPTR_COMPRESSION) /* Scan ROM pointer range for faster detection of "is 'p' a ROM pointer" * later on. */ if (1) { const void * const * ptrs = (const void * const *) duk_rom_compressed_pointers; duk__romptr_low = duk__romptr_high = (const void *) *ptrs; while (*ptrs) { if (*ptrs > duk__romptr_high) { duk__romptr_high = (const void *) *ptrs; } if (*ptrs < duk__romptr_low) { duk__romptr_low = (const void *) *ptrs; } ptrs++; } fprintf(stderr, "romptrs: low=%p high=%p\n", (const void *) duk__romptr_low, (const void *) duk__romptr_high); fflush(stderr); } #endif } void ajsheap_free(void) { if (ajsheap_ram != NULL) { free(ajsheap_ram); ajsheap_ram = NULL; } } /* AjsHeap.dump(), allows Ecmascript code to dump heap status at suitable * points. */ duk_ret_t ajsheap_dump_binding(duk_context *ctx) { AJS_HeapDump(); fflush(stdout); return 0; } void ajsheap_dump(void) { AJS_HeapDump(); fflush(stdout); } void ajsheap_register(duk_context *ctx) { duk_push_object(ctx); duk_push_c_function(ctx, ajsheap_dump_binding, 0); duk_put_prop_string(ctx, -2, "dump"); duk_put_global_string(ctx, "AjsHeap"); } /* * Wrapped ajs_heap.c alloc functions * * Used to write an alloc log. */ static FILE *ajsheap_alloc_log = NULL; static void ajsheap_write_alloc_log(const char *fmt, ...) { va_list ap; char buf[256]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf) - 1] = (char) 0; va_end(ap); if (ajsheap_alloc_log == NULL) { ajsheap_alloc_log = fopen("/tmp/ajduk-alloc-log.txt", "wb"); if (ajsheap_alloc_log == NULL) { fprintf(stderr, "WARNING: failed to write alloc log, ignoring\n"); fflush(stderr); return; } } (void) fwrite((const void *) buf, 1, strlen(buf), ajsheap_alloc_log); (void) fflush(ajsheap_alloc_log); } void *ajsheap_alloc_wrapped(void *udata, duk_size_t size) { void *ret = AJS_Alloc(udata, size); if (size > 0 && ret == NULL) { ajsheap_write_alloc_log("A FAIL %ld\n", (long) size); } else if (ret == NULL) { ajsheap_write_alloc_log("A NULL %ld\n", (long) size); } else { ajsheap_write_alloc_log("A %p %ld\n", ret, (long) size); } return ret; } void *ajsheap_realloc_wrapped(void *udata, void *ptr, duk_size_t size) { void *ret = AJS_Realloc(udata, ptr, size); if (size > 0 && ret == NULL) { if (ptr == NULL) { ajsheap_write_alloc_log("R NULL -1 FAIL %ld\n", (long) size); } else { ajsheap_write_alloc_log("R %p -1 FAIL %ld\n", ptr, (long) size); } } else if (ret == NULL) { if (ptr == NULL) { ajsheap_write_alloc_log("R NULL -1 NULL %ld\n", (long) size); } else { ajsheap_write_alloc_log("R %p -1 NULL %ld\n", ptr, (long) size); } } else { if (ptr == NULL) { ajsheap_write_alloc_log("R NULL -1 %p %ld\n", ret, (long) size); } else { ajsheap_write_alloc_log("R %p -1 %p %ld\n", ptr, ret, (long) size); } } return ret; } void ajsheap_free_wrapped(void *udata, void *ptr) { AJS_Free(udata, ptr); if (ptr == NULL) { } else { ajsheap_write_alloc_log("F %p -1\n", ptr); } } /* * Example pointer compression functions. * * 'base' is chosen so that no non-NULL pointer results in a zero result * which is reserved for NULL pointers. */ duk_uint16_t ajsheap_enc16(void *ud, void *p) { duk_uint32_t ret; char *base = (char *) ajsheap_ram - 4; #if defined(DUK__ROMPTR_COMPRESSION) if (p >= duk__romptr_low && p <= duk__romptr_high) { /* The if-condition should be the fastest possible check * for "is 'p' in ROM?". If pointer is in ROM, we'd like * to compress it quickly. Here we just scan a ~1K array * which is very bad for performance and for illustration * only. */ const void * const * ptrs = duk_rom_compressed_pointers; while (*ptrs) { if (*ptrs == p) { ret = DUK__ROMPTR_FIRST + (ptrs - duk_rom_compressed_pointers); #if 0 fprintf(stderr, "ajsheap_enc16: rom pointer: %p -> 0x%04lx\n", (void *) p, (long) ret); fflush(stderr); #endif return (duk_uint16_t) ret; } ptrs++; } /* We should really never be here: Duktape should only be * compressing pointers which are in the ROM compressed * pointers list, which are known at 'make dist' time. * We go on, causing a pointer compression error. */ fprintf(stderr, "ajsheap_enc16: rom pointer: %p could not be compressed, should never happen\n", (void *) p); fflush(stderr); } #endif /* Userdata is not needed in this case but would be useful if heap * pointer compression were used for multiple heaps. The userdata * allows the callback to distinguish between heaps and their base * pointers. * * If not needed, the userdata can be left out during compilation * by simply ignoring the userdata argument of the pointer encode * and decode macros. It is kept here so that any bugs in actually * providing the value inside Duktape are revealed during compilation. */ (void) ud; #if 1 /* Ensure that we always get the heap_udata given in heap creation. * (Useful for Duktape development, not needed for user programs.) */ if (ud != (void *) 0xdeadbeef) { fprintf(stderr, "invalid udata for ajsheap_enc16: %p\n", ud); fflush(stderr); } #endif if (p == NULL) { ret = 0; } else { ret = (duk_uint32_t) (((char *) p - base) >> 2); } #if 0 printf("ajsheap_enc16: %p -> %u\n", p, (unsigned int) ret); #endif if (ret > 0xffffUL) { fprintf(stderr, "Failed to compress pointer: %p (ret was %ld)\n", (void *) p, (long) ret); fflush(stderr); abort(); } #if defined(DUK__ROMPTR_COMPRESSION) if (ret >= DUK__ROMPTR_FIRST) { fprintf(stderr, "Failed to compress pointer, in 16-bit range but matches romptr range: %p (ret was %ld)\n", (void *) p, (long) ret); fflush(stderr); abort(); } #endif return (duk_uint16_t) ret; } void *ajsheap_dec16(void *ud, duk_uint16_t x) { void *ret; char *base = (char *) ajsheap_ram - 4; #if defined(DUK__ROMPTR_COMPRESSION) if (x >= DUK__ROMPTR_FIRST) { /* This is a blind lookup, could check index validity. * Duktape should never decompress a pointer which would * be out-of-bounds here. */ ret = (void *) ajduk__lose_const(duk_rom_compressed_pointers[x - DUK__ROMPTR_FIRST]); #if 0 fprintf(stderr, "ajsheap_dec16: rom pointer: 0x%04lx -> %p\n", (long) x, ret); fflush(stderr); #endif return ret; } #endif /* See userdata discussion in ajsheap_enc16(). */ (void) ud; #if 1 /* Ensure that we always get the heap_udata given in heap creation. */ if (ud != (void *) 0xdeadbeef) { fprintf(stderr, "invalid udata for ajsheap_dec16: %p\n", ud); fflush(stderr); } #endif if (x == 0) { ret = NULL; } else { ret = (void *) (base + (((duk_uint32_t) x) << 2)); } #if 0 printf("ajsheap_dec16: %u -> %p\n", (unsigned int) x, ret); #endif return ret; } /* * Simplified example of an external strings strategy where incoming strings * are written sequentially into a fixed, memory mapped flash area. * * The example first scans if the string is already in the flash (which may * happen if the same string is interned multiple times), then adds it to * flash if there is space. * * This example is too slow to be used in a real world application: there * should be e.g. a hash table to quickly check for strings that are already * present in the string data (similarly to how string interning works in * Duktape itself). */ static uint8_t ajsheap_strdata[65536]; static size_t ajsheap_strdata_used = 0; const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) { uint8_t *p, *p_end; uint8_t initial; uint8_t *ret; size_t left; (void) safe_print_chars; /* potentially unused */ if (len <= 3) { /* It's not worth it to make very small strings external, as * they would take the same space anyway. Also avoids zero * length degenerate case. */ return NULL; } /* * Check if we already have the string. Be careful to compare for * NUL terminator too, it is NOT present in 'ptr'. This algorithm * is too simplistic and way too slow for actual use. */ initial = ((const uint8_t *) ptr)[0]; for (p = ajsheap_strdata, p_end = p + ajsheap_strdata_used; p != p_end; p++) { if (*p != initial) { continue; } left = (size_t) (p_end - p); if (left >= len + 1 && memcmp(p, ptr, len) == 0 && p[len] == 0) { ret = p; #if 0 printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> existing %p (used=%ld)\n", (void *) ret, (long) ajsheap_strdata_used); #endif return ret; } } /* * Not present yet, check if we have space. Again, be careful to * ensure there is space for a NUL following the input data. */ if (ajsheap_strdata_used + len + 1 > sizeof(ajsheap_strdata)) { #if 0 printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> no space (used=%ld)\n", (long) ajsheap_strdata_used); #endif return NULL; } /* * There is space, add the string to our collection, being careful * to append the NUL. */ ret = ajsheap_strdata + ajsheap_strdata_used; memcpy(ret, ptr, len); ret[len] = (uint8_t) 0; ajsheap_strdata_used += len + 1; #if 0 printf("ajsheap_extstr_check_1: ptr=%p, len=%ld -> ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> %p (used=%ld)\n", (void *) ret, (long) ajsheap_strdata_used); #endif return (const void *) ret; } void ajsheap_extstr_free_1(const void *ptr) { (void) ptr; #if 0 printf("ajsheap_extstr_free_1: freeing extstr %p -> ", ptr); safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); printf("\n"); #endif } /* * Simplified example of an external strings strategy where a set of strings * is gathered during application compile time and baked into the application * binary. * * Duktape built-in strings are available from duk_build_meta.json, see * util/duk_meta_to_strarray.py. There may also be a lot of application * specific strings, e.g. those used by application specific APIs. These * must be gathered through some other means, see e.g. util/scan_strings.py. */ static const char *strdata_duk_builtin_strings[] = { /* * These strings are from util/duk_meta_to_strarray.py */ "Logger", "Thread", "Pointer", "Buffer", "DecEnv", "ObjEnv", "", "global", "Arguments", "JSON", "Math", "Error", "RegExp", "Date", "Number", "Boolean", "String", "Array", "Function", "Object", "Null", "Undefined", "{_func:true}", "{\x22" "_func\x22" ":true}", "{\x22" "_ninf\x22" ":true}", "{\x22" "_inf\x22" ":true}", "{\x22" "_nan\x22" ":true}", "{\x22" "_undef\x22" ":true}", "toLogString", "clog", "l", "n", "fatal", "error", "warn", "debug", "trace", "raw", "fmt", "current", "resume", "compact", "jc", "jx", "base64", "hex", "dec", "enc", "fin", "gc", "act", "info", "version", "env", "modLoaded", "modSearch", "errThrow", "errCreate", "compile", "\xff" "Regbase", "\xff" "Thread", "\xff" "Handler", "\xff" "Finalizer", "\xff" "Callee", "\xff" "Map", "\xff" "Args", "\xff" "This", "\xff" "Pc2line", "\xff" "Source", "\xff" "Varenv", "\xff" "Lexenv", "\xff" "Varmap", "\xff" "Formals", "\xff" "Bytecode", "\xff" "Next", "\xff" "Target", "\xff" "Value", "pointer", "buffer", "\xff" "Tracedata", "lineNumber", "fileName", "pc", "stack", "ThrowTypeError", "Duktape", "id", "require", "__proto__", "setPrototypeOf", "ownKeys", "enumerate", "deleteProperty", "has", "Proxy", "callee", "Invalid Date", "[...]", "\x0a" "\x09", " ", ",", "-0", "+0", "0", "-Infinity", "+Infinity", "Infinity", "object", "string", "number", "boolean", "undefined", "stringify", "tan", "sqrt", "sin", "round", "random", "pow", "min", "max", "log", "floor", "exp", "cos", "ceil", "atan2", "atan", "asin", "acos", "abs", "SQRT2", "SQRT1_2", "PI", "LOG10E", "LOG2E", "LN2", "LN10", "E", "message", "name", "input", "index", "(?:)", "lastIndex", "multiline", "ignoreCase", "source", "test", "exec", "toGMTString", "setYear", "getYear", "toJSON", "toISOString", "toUTCString", "setUTCFullYear", "setFullYear", "setUTCMonth", "setMonth", "setUTCDate", "setDate", "setUTCHours", "setHours", "setUTCMinutes", "setMinutes", "setUTCSeconds", "setSeconds", "setUTCMilliseconds", "setMilliseconds", "setTime", "getTimezoneOffset", "getUTCMilliseconds", "getMilliseconds", "getUTCSeconds", "getSeconds", "getUTCMinutes", "getMinutes", "getUTCHours", "getHours", "getUTCDay", "getDay", "getUTCDate", "getDate", "getUTCMonth", "getMonth", "getUTCFullYear", "getFullYear", "getTime", "toLocaleTimeString", "toLocaleDateString", "toTimeString", "toDateString", "now", "UTC", "parse", "toPrecision", "toExponential", "toFixed", "POSITIVE_INFINITY", "NEGATIVE_INFINITY", "NaN", "MIN_VALUE", "MAX_VALUE", "substr", "trim", "toLocaleUpperCase", "toUpperCase", "toLocaleLowerCase", "toLowerCase", "substring", "split", "search", "replace", "match", "localeCompare", "charCodeAt", "charAt", "fromCharCode", "reduceRight", "reduce", "filter", "map", "forEach", "some", "every", "lastIndexOf", "indexOf", "unshift", "splice", "sort", "slice", "shift", "reverse", "push", "pop", "join", "concat", "isArray", "arguments", "caller", "bind", "call", "apply", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "valueOf", "toLocaleString", "toString", "constructor", "set", "get", "enumerable", "configurable", "writable", "value", "keys", "isExtensible", "isFrozen", "isSealed", "preventExtensions", "freeze", "seal", "defineProperties", "defineProperty", "create", "getOwnPropertyNames", "getOwnPropertyDescriptor", "getPrototypeOf", "prototype", "length", "alert", "print", "unescape", "escape", "encodeURIComponent", "encodeURI", "decodeURIComponent", "decodeURI", "isFinite", "isNaN", "parseFloat", "parseInt", "eval", "URIError", "TypeError", "SyntaxError", "ReferenceError", "RangeError", "EvalError", "break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else", "finally", "for", "function", "if", "in", "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "class", "const", "enum", "export", "extends", "import", "super", "null", "true", "false", "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", /* * These strings are manually added, and would be gathered in some * application specific manner. */ "foo", "bar", "quux", "enableFrob", "disableFrob" /* ... */ }; const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len) { int i, n; (void) safe_print_chars; /* potentially unused */ /* Linear scan. An actual implementation would need some acceleration * structure, e.g. select a sublist based on first character. * * NOTE: input string (behind 'ptr' with 'len' bytes) DOES NOT have a * trailing NUL character. Any strings returned from this function * MUST have a trailing NUL character. */ n = (int) (sizeof(strdata_duk_builtin_strings) / sizeof(const char *)); for (i = 0; i < n; i++) { const char *str; str = strdata_duk_builtin_strings[i]; if (strlen(str) == len && memcmp(ptr, (const void *) str, len) == 0) { #if 0 printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> constant string index %ld\n", (long) i); #endif return (void *) ajduk__lose_const(strdata_duk_builtin_strings[i]); } } #if 0 printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> not found\n"); #endif return NULL; } void ajsheap_extstr_free_2(const void *ptr) { (void) ptr; #if 0 printf("ajsheap_extstr_free_2: freeing extstr %p -> ", ptr); safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); printf("\n"); #endif } /* * External strings strategy intended for valgrind testing: external strings * are allocated using malloc()/free() so that valgrind can be used to ensure * that strings are e.g. freed exactly once. */ const void *ajsheap_extstr_check_3(const void *ptr, duk_size_t len) { duk_uint8_t *ret; (void) safe_print_chars; /* potentially unused */ ret = malloc((size_t) len + 1); if (ret == NULL) { #if 0 printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> malloc failed, return NULL\n"); #endif return (const void *) NULL; } if (len > 0) { memcpy((void *) ret, ptr, (size_t) len); } ret[len] = (duk_uint8_t) 0; #if 0 printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ", (void *) ptr, (long) len); safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); printf(" -> %p\n", (void *) ret); #endif return (const void *) ret; } void ajsheap_extstr_free_3(const void *ptr) { (void) ptr; #if 0 printf("ajsheap_extstr_free_3: freeing extstr %p -> ", ptr); safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); printf("\n"); #endif free((void *) ajduk__lose_const(ptr)); } /* * Execution timeout example */ #define AJSHEAP_EXEC_TIMEOUT 5 /* seconds */ static time_t curr_pcall_start = 0; static long exec_timeout_check_counter = 0; void ajsheap_start_exec_timeout(void) { curr_pcall_start = time(NULL); } void ajsheap_clear_exec_timeout(void) { curr_pcall_start = 0; } duk_bool_t ajsheap_exec_timeout_check(void *udata) { time_t now = time(NULL); time_t diff = now - curr_pcall_start; (void) udata; /* not needed */ exec_timeout_check_counter++; #if 0 printf("exec timeout check %ld: now=%ld, start=%ld, diff=%ld\n", (long) exec_timeout_check_counter, (long) now, (long) curr_pcall_start, (long) diff); fflush(stdout); #endif if (curr_pcall_start == 0) { /* protected call not yet running */ return 0; } if (diff > AJSHEAP_EXEC_TIMEOUT) { return 1; } return 0; } #else /* DUK_CMDLINE_AJSHEAP */ int ajs_dummy = 0; /* to avoid empty source file */ #endif /* DUK_CMDLINE_AJSHEAP */