pkpy.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. #include "pocketpy/interpreter/objectpool.h"
  2. #include "pocketpy/objects/base.h"
  3. #include "pocketpy/pocketpy.h"
  4. #include "pocketpy/common/utils.h"
  5. #include "pocketpy/common/sstream.h"
  6. #include "pocketpy/interpreter/vm.h"
  7. #include "pocketpy/common/threads.h"
  8. #include <time.h>
  9. #define DEF_TVALUE_METHODS(T, Field) \
  10. static bool TValue_##T##__new__(int argc, py_Ref argv) { \
  11. PY_CHECK_ARGC(2); \
  12. PY_CHECK_ARG_TYPE(0, tp_type); \
  13. PY_CHECK_ARG_TYPE(1, tp_##T); \
  14. *py_retval() = (py_TValue){ \
  15. .type = py_totype(&argv[0]), \
  16. .is_ptr = false, \
  17. .Field = py_to##T(&argv[1]), \
  18. }; \
  19. return true; \
  20. } \
  21. static bool TValue_##T##_value(int argc, py_Ref argv) { \
  22. PY_CHECK_ARGC(1); \
  23. py_new##T(py_retval(), argv->Field); \
  24. return true; \
  25. } \
  26. static bool TValue_##T##__repr__(int argc, py_Ref argv) { \
  27. PY_CHECK_ARGC(1); \
  28. py_newstr(py_retval(), "<TValue_" #T " object>"); \
  29. return true; \
  30. }
  31. DEF_TVALUE_METHODS(int, _i64)
  32. DEF_TVALUE_METHODS(float, _f64)
  33. DEF_TVALUE_METHODS(vec2, _vec2)
  34. DEF_TVALUE_METHODS(vec2i, _vec2i)
  35. static bool pkpy_memory_usage(int argc, py_Ref argv) {
  36. PY_CHECK_ARGC(0);
  37. ManagedHeap* heap = &pk_current_vm->heap;
  38. c11_string* small_objects_usage = MultiPool__summary(&heap->small_objects);
  39. int large_object_count = heap->large_objects.length;
  40. c11_sbuf buf;
  41. c11_sbuf__ctor(&buf);
  42. c11_sbuf__write_cstr(&buf, "== heap.small_objects ==\n");
  43. c11_sbuf__write_cstr(&buf, small_objects_usage->data);
  44. c11_sbuf__write_cstr(&buf, "== heap.large_objects ==\n");
  45. pk_sprintf(&buf, "len(large_objects)=%d\n", large_object_count);
  46. c11_sbuf__write_cstr(&buf, "== heap.gc ==\n");
  47. pk_sprintf(&buf, "gc_counter=%d\n", heap->gc_counter);
  48. pk_sprintf(&buf, "gc_threshold=%d", heap->gc_threshold);
  49. // c11_sbuf__write_cstr(&buf, "== vm.pool_frame ==\n");
  50. c11_sbuf__py_submit(&buf, py_retval());
  51. c11_string__delete(small_objects_usage);
  52. return true;
  53. }
  54. static bool pkpy_is_user_defined_type(int argc, py_Ref argv) {
  55. PY_CHECK_ARGC(1);
  56. PY_CHECK_ARG_TYPE(0, tp_type);
  57. py_TypeInfo* ti = py_touserdata(argv);
  58. py_newbool(py_retval(), ti->is_python);
  59. return true;
  60. }
  61. static bool pkpy_enable_full_buffering_mode(int argc, py_Ref argv) {
  62. PY_CHECK_ARGC(0);
  63. static char buf[1024 * 128];
  64. setvbuf(stdout, buf, _IOFBF, sizeof(buf));
  65. py_newnone(py_retval());
  66. return true;
  67. }
  68. static bool pkpy_currentvm(int argc, py_Ref argv) {
  69. PY_CHECK_ARGC(0);
  70. py_newint(py_retval(), py_currentvm());
  71. return true;
  72. }
  73. #if PK_ENABLE_WATCHDOG
  74. void py_watchdog_begin(py_i64 timeout) {
  75. WatchdogInfo* info = &pk_current_vm->watchdog_info;
  76. info->max_reset_time = clock() + (timeout * (CLOCKS_PER_SEC / 1000));
  77. }
  78. void py_watchdog_end() {
  79. WatchdogInfo* info = &pk_current_vm->watchdog_info;
  80. info->max_reset_time = 0;
  81. }
  82. static bool pkpy_watchdog_begin(int argc, py_Ref argv) {
  83. PY_CHECK_ARGC(1);
  84. PY_CHECK_ARG_TYPE(0, tp_int);
  85. py_watchdog_begin(py_toint(argv));
  86. py_newnone(py_retval());
  87. return true;
  88. }
  89. static bool pkpy_watchdog_end(int argc, py_Ref argv) {
  90. PY_CHECK_ARGC(0);
  91. py_watchdog_end();
  92. py_newnone(py_retval());
  93. return true;
  94. }
  95. #endif
  96. #if PK_ENABLE_THREADS
  97. typedef struct c11_ComputeThread c11_ComputeThread;
  98. typedef struct {
  99. c11_ComputeThread* self;
  100. char* eval_src;
  101. unsigned char* args_data;
  102. int args_size;
  103. unsigned char* kwargs_data;
  104. int kwargs_size;
  105. } ComputeThreadJobCall;
  106. typedef struct {
  107. c11_ComputeThread* self;
  108. char* source;
  109. enum py_CompileMode mode;
  110. } ComputeThreadJobExec;
  111. static void ComputeThreadJobCall__dtor(void* arg) {
  112. ComputeThreadJobCall* self = arg;
  113. PK_FREE(self->eval_src);
  114. PK_FREE(self->args_data);
  115. PK_FREE(self->kwargs_data);
  116. }
  117. static void ComputeThreadJobExec__dtor(void* arg) {
  118. ComputeThreadJobExec* self = arg;
  119. PK_FREE(self->source);
  120. }
  121. typedef struct c11_ComputeThread {
  122. int vm_index;
  123. atomic_bool is_done;
  124. unsigned char* last_retval_data;
  125. int last_retval_size;
  126. char* last_error;
  127. c11_thrd_t thread;
  128. void* job;
  129. void (*job_dtor)(void*);
  130. } c11_ComputeThread;
  131. static void
  132. c11_ComputeThread__reset_job(c11_ComputeThread* self, void* job, void (*job_dtor)(void*)) {
  133. if(self->job) {
  134. self->job_dtor(self->job);
  135. PK_FREE(self->job);
  136. }
  137. self->job = job;
  138. self->job_dtor = job_dtor;
  139. }
  140. static bool _pk_compute_thread_flags[16];
  141. static void c11_ComputeThread__dtor(c11_ComputeThread* self) {
  142. if(!atomic_load(&self->is_done)) {
  143. c11__abort("ComputeThread(%d) is not done yet!! But the object was deleted.",
  144. self->vm_index);
  145. }
  146. if(self->last_retval_data) PK_FREE(self->last_retval_data);
  147. if(self->last_error) PK_FREE(self->last_error);
  148. c11_ComputeThread__reset_job(self, NULL, NULL);
  149. _pk_compute_thread_flags[self->vm_index] = false;
  150. }
  151. static void c11_ComputeThread__on_job_begin(c11_ComputeThread* self) {
  152. if(self->last_retval_data) {
  153. PK_FREE(self->last_retval_data);
  154. self->last_retval_data = NULL;
  155. self->last_retval_size = 0;
  156. }
  157. if(self->last_error) {
  158. PK_FREE(self->last_error);
  159. self->last_error = NULL;
  160. }
  161. py_switchvm(self->vm_index);
  162. }
  163. static bool ComputeThread__new__(int argc, py_Ref argv) {
  164. c11_ComputeThread* self =
  165. py_newobject(py_retval(), py_totype(argv), 0, sizeof(c11_ComputeThread));
  166. self->vm_index = 0;
  167. atomic_store(&self->is_done, true);
  168. self->last_retval_data = NULL;
  169. self->last_retval_size = 0;
  170. self->last_error = NULL;
  171. self->job = NULL;
  172. self->job_dtor = NULL;
  173. return true;
  174. }
  175. static bool ComputeThread__init__(int argc, py_Ref argv) {
  176. PY_CHECK_ARGC(2);
  177. PY_CHECK_ARG_TYPE(1, tp_int);
  178. c11_ComputeThread* self = py_touserdata(py_arg(0));
  179. int index = py_toint(py_arg(1));
  180. if(index >= 1 && index < 16) {
  181. if(_pk_compute_thread_flags[index]) {
  182. return ValueError("vm_index %d is already in use", index);
  183. }
  184. _pk_compute_thread_flags[index] = true;
  185. self->vm_index = index;
  186. } else {
  187. return ValueError("vm_index %d is out of range", index);
  188. }
  189. py_newnone(py_retval());
  190. return true;
  191. }
  192. static bool ComputeThread_is_done(int argc, py_Ref argv) {
  193. PY_CHECK_ARGC(1);
  194. c11_ComputeThread* self = py_touserdata(argv);
  195. bool value = atomic_load(&self->is_done);
  196. py_newbool(py_retval(), value);
  197. return true;
  198. }
  199. static bool ComputeThread_wait_for_done(int argc, py_Ref argv) {
  200. PY_CHECK_ARGC(1);
  201. c11_ComputeThread* self = py_touserdata(argv);
  202. while(!atomic_load(&self->is_done)) {
  203. c11_thrd_yield();
  204. }
  205. py_newnone(py_retval());
  206. return true;
  207. }
  208. static bool ComputeThread_last_error(int argc, py_Ref argv) {
  209. PY_CHECK_ARGC(1);
  210. c11_ComputeThread* self = py_touserdata(argv);
  211. if(!atomic_load(&self->is_done)) return OSError("thread is not done yet");
  212. if(self->last_error) {
  213. py_newstr(py_retval(), self->last_error);
  214. } else {
  215. py_newnone(py_retval());
  216. }
  217. return true;
  218. }
  219. static bool ComputeThread_last_retval(int argc, py_Ref argv) {
  220. PY_CHECK_ARGC(1);
  221. c11_ComputeThread* self = py_touserdata(argv);
  222. if(!atomic_load(&self->is_done)) return OSError("thread is not done yet");
  223. if(self->last_retval_data == NULL) return ValueError("no retval available");
  224. return py_pickle_loads(self->last_retval_data, self->last_retval_size);
  225. }
  226. static c11_thrd_retval_t ComputeThreadJob_call(void* arg) {
  227. ComputeThreadJobCall* job = arg;
  228. c11_ComputeThread* self = job->self;
  229. c11_ComputeThread__on_job_begin(self);
  230. py_StackRef p0 = py_peek(0);
  231. if(!py_pusheval(job->eval_src, NULL)) goto __ERROR;
  232. // [callable]
  233. if(!py_pickle_loads(job->args_data, job->args_size)) goto __ERROR;
  234. py_push(py_retval());
  235. // [callable, args]
  236. if(!py_pickle_loads(job->kwargs_data, job->kwargs_size)) goto __ERROR;
  237. py_push(py_retval());
  238. // [callable, args, kwargs]
  239. if(!py_smarteval("_0(*_1, **_2)", NULL, py_peek(-3), py_peek(-2), py_peek(-1))) goto __ERROR;
  240. py_shrink(3);
  241. if(!py_pickle_dumps(py_retval())) goto __ERROR;
  242. int retval_size;
  243. unsigned char* retval_data = py_tobytes(py_retval(), &retval_size);
  244. self->last_retval_data = c11_memdup(retval_data, retval_size);
  245. self->last_retval_size = retval_size;
  246. atomic_store(&self->is_done, true);
  247. return (c11_thrd_retval_t)0;
  248. __ERROR:
  249. self->last_error = py_formatexc();
  250. atomic_store(&self->is_done, true);
  251. py_clearexc(p0);
  252. py_newnone(py_retval());
  253. return (c11_thrd_retval_t)0;
  254. }
  255. static c11_thrd_retval_t ComputeThreadJob_exec(void* arg) {
  256. ComputeThreadJobExec* job = arg;
  257. c11_ComputeThread* self = job->self;
  258. c11_ComputeThread__on_job_begin(self);
  259. py_StackRef p0 = py_peek(0);
  260. if(!py_exec(job->source, "<job>", job->mode, NULL)) goto __ERROR;
  261. if(!py_pickle_dumps(py_retval())) goto __ERROR;
  262. int retval_size;
  263. unsigned char* retval_data = py_tobytes(py_retval(), &retval_size);
  264. self->last_retval_data = c11_memdup(retval_data, retval_size);
  265. self->last_retval_size = retval_size;
  266. atomic_store(&self->is_done, true);
  267. return (c11_thrd_retval_t)0;
  268. __ERROR:
  269. self->last_error = py_formatexc();
  270. atomic_store(&self->is_done, true);
  271. py_clearexc(p0);
  272. return (c11_thrd_retval_t)0;
  273. }
  274. static bool ComputeThread_submit_exec(int argc, py_Ref argv) {
  275. PY_CHECK_ARGC(2);
  276. c11_ComputeThread* self = py_touserdata(py_arg(0));
  277. if(!atomic_load(&self->is_done)) return OSError("thread is not done yet");
  278. PY_CHECK_ARG_TYPE(1, tp_str);
  279. const char* source = py_tostr(py_arg(1));
  280. /**************************/
  281. ComputeThreadJobExec* job = PK_MALLOC(sizeof(ComputeThreadJobExec));
  282. job->self = self;
  283. job->source = c11_strdup(source);
  284. job->mode = EXEC_MODE;
  285. c11_ComputeThread__reset_job(self, job, ComputeThreadJobExec__dtor);
  286. /**************************/
  287. atomic_store(&self->is_done, false);
  288. bool ok = c11_thrd_create(&self->thread, ComputeThreadJob_exec, job);
  289. if(!ok) {
  290. atomic_store(&self->is_done, true);
  291. return OSError("thrd_create() failed");
  292. }
  293. py_newnone(py_retval());
  294. return true;
  295. }
  296. static bool ComputeThread_submit_eval(int argc, py_Ref argv) {
  297. PY_CHECK_ARGC(2);
  298. c11_ComputeThread* self = py_touserdata(py_arg(0));
  299. if(!atomic_load(&self->is_done)) return OSError("thread is not done yet");
  300. PY_CHECK_ARG_TYPE(1, tp_str);
  301. const char* source = py_tostr(py_arg(1));
  302. /**************************/
  303. ComputeThreadJobExec* job = PK_MALLOC(sizeof(ComputeThreadJobExec));
  304. job->self = self;
  305. job->source = c11_strdup(source);
  306. job->mode = EVAL_MODE;
  307. c11_ComputeThread__reset_job(self, job, ComputeThreadJobExec__dtor);
  308. /**************************/
  309. atomic_store(&self->is_done, false);
  310. bool ok = c11_thrd_create(&self->thread, ComputeThreadJob_exec, job);
  311. if(!ok) {
  312. atomic_store(&self->is_done, true);
  313. return OSError("thrd_create() failed");
  314. }
  315. py_newnone(py_retval());
  316. return true;
  317. }
  318. static bool ComputeThread_submit_call(int argc, py_Ref argv) {
  319. PY_CHECK_ARGC(4);
  320. c11_ComputeThread* self = py_touserdata(py_arg(0));
  321. if(!atomic_load(&self->is_done)) return OSError("thread is not done yet");
  322. PY_CHECK_ARG_TYPE(1, tp_str);
  323. PY_CHECK_ARG_TYPE(2, tp_tuple);
  324. PY_CHECK_ARG_TYPE(3, tp_dict);
  325. // eval_src
  326. const char* eval_src = py_tostr(py_arg(1));
  327. // *args
  328. if(!py_pickle_dumps(py_arg(2))) return false;
  329. int args_size;
  330. unsigned char* args_data = py_tobytes(py_retval(), &args_size);
  331. // *kwargs
  332. if(!py_pickle_dumps(py_arg(3))) return false;
  333. int kwargs_size;
  334. unsigned char* kwargs_data = py_tobytes(py_retval(), &kwargs_size);
  335. /**************************/
  336. ComputeThreadJobCall* job = PK_MALLOC(sizeof(ComputeThreadJobCall));
  337. job->self = self;
  338. job->eval_src = c11_strdup(eval_src);
  339. job->args_data = c11_memdup(args_data, args_size);
  340. job->args_size = args_size;
  341. job->kwargs_data = c11_memdup(kwargs_data, kwargs_size);
  342. job->kwargs_size = kwargs_size;
  343. c11_ComputeThread__reset_job(self, job, ComputeThreadJobCall__dtor);
  344. /**************************/
  345. atomic_store(&self->is_done, false);
  346. bool ok = c11_thrd_create(&self->thread, ComputeThreadJob_call, job);
  347. if(!ok) {
  348. atomic_store(&self->is_done, true);
  349. return OSError("thrd_create() failed");
  350. }
  351. py_newnone(py_retval());
  352. return true;
  353. }
  354. static bool c11_ComputeThread__exec_blocked(c11_ComputeThread* self,
  355. const char* source,
  356. enum py_CompileMode mode) {
  357. if(!atomic_load(&self->is_done)) return OSError("thread is not done yet");
  358. atomic_store(&self->is_done, false);
  359. char* err = NULL;
  360. int old_vm_index = py_currentvm();
  361. py_switchvm(self->vm_index);
  362. py_StackRef p0 = py_peek(0);
  363. if(!py_exec(source, "<job_blocked>", mode, NULL)) goto __ERROR;
  364. if(!py_pickle_dumps(py_retval())) goto __ERROR;
  365. int retval_size;
  366. unsigned char* retval_data = py_tobytes(py_retval(), &retval_size);
  367. py_switchvm(old_vm_index);
  368. bool ok = py_pickle_loads(retval_data, retval_size);
  369. atomic_store(&self->is_done, true);
  370. return ok;
  371. __ERROR:
  372. err = py_formatexc();
  373. py_clearexc(p0);
  374. py_switchvm(old_vm_index);
  375. atomic_store(&self->is_done, true);
  376. RuntimeError("c11_ComputeThread__exec_blocked() failed:\n%s", err);
  377. PK_FREE(err);
  378. return false;
  379. }
  380. static bool ComputeThread_exec(int argc, py_Ref argv) {
  381. PY_CHECK_ARGC(2);
  382. c11_ComputeThread* self = py_touserdata(py_arg(0));
  383. PY_CHECK_ARG_TYPE(1, tp_str);
  384. const char* source = py_tostr(py_arg(1));
  385. return c11_ComputeThread__exec_blocked(self, source, EXEC_MODE);
  386. }
  387. static bool ComputeThread_eval(int argc, py_Ref argv) {
  388. PY_CHECK_ARGC(2);
  389. c11_ComputeThread* self = py_touserdata(py_arg(0));
  390. PY_CHECK_ARG_TYPE(1, tp_str);
  391. const char* source = py_tostr(py_arg(1));
  392. return c11_ComputeThread__exec_blocked(self, source, EVAL_MODE);
  393. }
  394. static void pk_ComputeThread__register(py_Ref mod) {
  395. py_Type type = py_newtype("ComputeThread", tp_object, mod, (py_Dtor)c11_ComputeThread__dtor);
  396. py_bindmagic(type, __new__, ComputeThread__new__);
  397. py_bindmagic(type, __init__, ComputeThread__init__);
  398. py_bindproperty(type, "is_done", ComputeThread_is_done, NULL);
  399. py_bindmethod(type, "wait_for_done", ComputeThread_wait_for_done);
  400. py_bindmethod(type, "last_error", ComputeThread_last_error);
  401. py_bindmethod(type, "last_retval", ComputeThread_last_retval);
  402. py_bindmethod(type, "submit_exec", ComputeThread_submit_exec);
  403. py_bindmethod(type, "submit_eval", ComputeThread_submit_eval);
  404. py_bind(py_tpobject(type),
  405. "submit_call(self, eval_src, *args, **kwargs)",
  406. ComputeThread_submit_call);
  407. py_bindmethod(type, "exec", ComputeThread_exec);
  408. py_bindmethod(type, "eval", ComputeThread_eval);
  409. }
  410. #endif // PK_ENABLE_THREADS
  411. static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
  412. assert(dict->type == tp_dict);
  413. py_TValue tmp;
  414. py_newint(&tmp, val);
  415. py_dict_setitem_by_str(dict, key, &tmp);
  416. }
  417. void pk__add_module_pkpy() {
  418. py_Ref mod = py_newmodule("pkpy");
  419. py_Type ttype;
  420. py_Ref TValue_dict = py_pushtmp();
  421. py_newdict(TValue_dict);
  422. ttype = pk_newtype("TValue_int", tp_object, mod, NULL, false, false);
  423. py_bindmagic(ttype, __new__, TValue_int__new__);
  424. py_bindmagic(ttype, __repr__, TValue_int__repr__);
  425. py_bindproperty(ttype, "value", TValue_int_value, NULL);
  426. py_dict_setitem(TValue_dict, py_tpobject(tp_int), py_tpobject(ttype));
  427. ttype = pk_newtype("TValue_float", tp_object, mod, NULL, false, false);
  428. py_bindmagic(ttype, __new__, TValue_float__new__);
  429. py_bindmagic(ttype, __repr__, TValue_float__repr__);
  430. py_bindproperty(ttype, "value", TValue_float_value, NULL);
  431. py_dict_setitem(TValue_dict, py_tpobject(tp_float), py_tpobject(ttype));
  432. ttype = pk_newtype("TValue_vec2", tp_object, mod, NULL, false, false);
  433. py_bindmagic(ttype, __new__, TValue_vec2__new__);
  434. py_bindmagic(ttype, __repr__, TValue_vec2__repr__);
  435. py_bindproperty(ttype, "value", TValue_vec2_value, NULL);
  436. py_dict_setitem(TValue_dict, py_tpobject(tp_vec2), py_tpobject(ttype));
  437. ttype = pk_newtype("TValue_vec2i", tp_object, mod, NULL, false, false);
  438. py_bindmagic(ttype, __new__, TValue_vec2i__new__);
  439. py_bindmagic(ttype, __repr__, TValue_vec2i__repr__);
  440. py_bindproperty(ttype, "value", TValue_vec2i_value, NULL);
  441. py_dict_setitem(TValue_dict, py_tpobject(tp_vec2i), py_tpobject(ttype));
  442. py_setdict(mod, py_name("TValue"), TValue_dict);
  443. py_pop();
  444. py_bindfunc(mod, "memory_usage", pkpy_memory_usage);
  445. py_bindfunc(mod, "is_user_defined_type", pkpy_is_user_defined_type);
  446. py_bindfunc(mod, "enable_full_buffering_mode", pkpy_enable_full_buffering_mode);
  447. py_bindfunc(mod, "currentvm", pkpy_currentvm);
  448. #if PK_ENABLE_WATCHDOG
  449. py_bindfunc(mod, "watchdog_begin", pkpy_watchdog_begin);
  450. py_bindfunc(mod, "watchdog_end", pkpy_watchdog_end);
  451. #endif
  452. #if PK_ENABLE_THREADS
  453. pk_ComputeThread__register(mod);
  454. #endif
  455. py_Ref configmacros = py_emplacedict(mod, py_name("configmacros"));
  456. py_newdict(configmacros);
  457. pkpy_configmacros_add(configmacros, "PK_ENABLE_OS", PK_ENABLE_OS);
  458. pkpy_configmacros_add(configmacros, "PK_ENABLE_THREADS", PK_ENABLE_THREADS);
  459. pkpy_configmacros_add(configmacros, "PK_ENABLE_DETERMINISM", PK_ENABLE_DETERMINISM);
  460. pkpy_configmacros_add(configmacros, "PK_ENABLE_WATCHDOG", PK_ENABLE_WATCHDOG);
  461. pkpy_configmacros_add(configmacros, "PK_GC_MIN_THRESHOLD", PK_GC_MIN_THRESHOLD);
  462. pkpy_configmacros_add(configmacros, "PK_VM_STACK_SIZE", PK_VM_STACK_SIZE);
  463. }
  464. #undef DEF_TVALUE_METHODS