blob: 49d3b48e387928f4cea6fb91477549c50b3cd260 [file] [log] [blame]
Eric Snow7f8bfc92018-01-29 18:23:44 -07001
2/* interpreters module */
3/* low-level access to interpreter primitives */
4
5#include "Python.h"
6#include "frameobject.h"
7#include "internal/pystate.h"
8
9
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080010static char *
11_copy_raw_string(PyObject *strobj)
12{
13 const char *str = PyUnicode_AsUTF8(strobj);
14 if (str == NULL) {
15 return NULL;
16 }
17 char *copied = PyMem_Malloc(strlen(str)+1);
18 if (str == NULL) {
19 PyErr_NoMemory();
20 return NULL;
21 }
22 strcpy(copied, str);
23 return copied;
24}
25
Eric Snow7f8bfc92018-01-29 18:23:44 -070026static PyInterpreterState *
27_get_current(void)
28{
29 PyThreadState *tstate = PyThreadState_Get();
30 // PyThreadState_Get() aborts if lookup fails, so we don't need
31 // to check the result for NULL.
32 return tstate->interp;
33}
34
35static int64_t
36_coerce_id(PyObject *id)
37{
38 id = PyNumber_Long(id);
39 if (id == NULL) {
40 if (PyErr_ExceptionMatches(PyExc_TypeError)) {
41 PyErr_SetString(PyExc_TypeError,
42 "'id' must be a non-negative int");
43 }
44 else {
45 PyErr_SetString(PyExc_ValueError,
46 "'id' must be a non-negative int");
47 }
48 return -1;
49 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080050 int64_t cid = PyLong_AsLongLong(id);
51 Py_DECREF(id);
Eric Snow7f8bfc92018-01-29 18:23:44 -070052 if (cid == -1 && PyErr_Occurred() != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080053 if (!PyErr_ExceptionMatches(PyExc_OverflowError)) {
54 PyErr_SetString(PyExc_ValueError,
55 "'id' must be a non-negative int");
56 }
Eric Snow7f8bfc92018-01-29 18:23:44 -070057 return -1;
58 }
59 if (cid < 0) {
60 PyErr_SetString(PyExc_ValueError,
61 "'id' must be a non-negative int");
62 return -1;
63 }
Eric Snow7f8bfc92018-01-29 18:23:44 -070064 return cid;
65}
66
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080067
Eric Snow7f8bfc92018-01-29 18:23:44 -070068/* data-sharing-specific code ***********************************************/
69
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080070struct _sharednsitem {
71 char *name;
Eric Snow7f8bfc92018-01-29 18:23:44 -070072 _PyCrossInterpreterData data;
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080073};
Eric Snow7f8bfc92018-01-29 18:23:44 -070074
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080075static int
76_sharednsitem_init(struct _sharednsitem *item, PyObject *key, PyObject *value)
Eric Snow7f8bfc92018-01-29 18:23:44 -070077{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080078 item->name = _copy_raw_string(key);
79 if (item->name == NULL) {
80 return -1;
Eric Snow7f8bfc92018-01-29 18:23:44 -070081 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080082 if (_PyObject_GetCrossInterpreterData(value, &item->data) != 0) {
83 return -1;
84 }
85 return 0;
Eric Snow7f8bfc92018-01-29 18:23:44 -070086}
87
Miss Islington (bot)f33eced2018-02-02 21:38:57 -080088static void
89_sharednsitem_clear(struct _sharednsitem *item)
90{
91 if (item->name != NULL) {
92 PyMem_Free(item->name);
93 }
94 _PyCrossInterpreterData_Release(&item->data);
95}
96
97static int
98_sharednsitem_apply(struct _sharednsitem *item, PyObject *ns)
99{
100 PyObject *name = PyUnicode_FromString(item->name);
101 if (name == NULL) {
102 return -1;
103 }
104 PyObject *value = _PyCrossInterpreterData_NewObject(&item->data);
105 if (value == NULL) {
106 Py_DECREF(name);
107 return -1;
108 }
109 int res = PyDict_SetItem(ns, name, value);
110 Py_DECREF(name);
111 Py_DECREF(value);
112 return res;
113}
114
115typedef struct _sharedns {
116 Py_ssize_t len;
117 struct _sharednsitem* items;
118} _sharedns;
119
120static _sharedns *
121_sharedns_new(Py_ssize_t len)
122{
123 _sharedns *shared = PyMem_NEW(_sharedns, 1);
124 if (shared == NULL) {
125 PyErr_NoMemory();
126 return NULL;
127 }
128 shared->len = len;
129 shared->items = PyMem_NEW(struct _sharednsitem, len);
130 if (shared->items == NULL) {
131 PyErr_NoMemory();
132 PyMem_Free(shared);
133 return NULL;
134 }
135 return shared;
136}
137
138static void
139_sharedns_free(_sharedns *shared)
140{
141 for (Py_ssize_t i=0; i < shared->len; i++) {
142 _sharednsitem_clear(&shared->items[i]);
143 }
144 PyMem_Free(shared->items);
145 PyMem_Free(shared);
146}
147
148static _sharedns *
149_get_shared_ns(PyObject *shareable)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700150{
151 if (shareable == NULL || shareable == Py_None) {
Eric Snow7f8bfc92018-01-29 18:23:44 -0700152 return NULL;
153 }
154 Py_ssize_t len = PyDict_Size(shareable);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700155 if (len == 0) {
156 return NULL;
157 }
158
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800159 _sharedns *shared = _sharedns_new(len);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700160 if (shared == NULL) {
161 return NULL;
162 }
Eric Snow7f8bfc92018-01-29 18:23:44 -0700163 Py_ssize_t pos = 0;
164 for (Py_ssize_t i=0; i < len; i++) {
165 PyObject *key, *value;
166 if (PyDict_Next(shareable, &pos, &key, &value) == 0) {
167 break;
168 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800169 if (_sharednsitem_init(&shared->items[i], key, value) != 0) {
Eric Snow7f8bfc92018-01-29 18:23:44 -0700170 break;
171 }
Eric Snow7f8bfc92018-01-29 18:23:44 -0700172 }
173 if (PyErr_Occurred()) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800174 _sharedns_free(shared);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700175 return NULL;
176 }
177 return shared;
178}
179
180static int
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800181_sharedns_apply(_sharedns *shared, PyObject *ns)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700182{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800183 for (Py_ssize_t i=0; i < shared->len; i++) {
184 if (_sharednsitem_apply(&shared->items[i], ns) != 0) {
185 return -1;
186 }
Eric Snow7f8bfc92018-01-29 18:23:44 -0700187 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800188 return 0;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700189}
190
191// Ultimately we'd like to preserve enough information about the
192// exception and traceback that we could re-constitute (or at least
193// simulate, a la traceback.TracebackException), and even chain, a copy
194// of the exception in the calling interpreter.
195
196typedef struct _sharedexception {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800197 char *name;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700198 char *msg;
199} _sharedexception;
200
201static _sharedexception *
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800202_sharedexception_new(void)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700203{
204 _sharedexception *err = PyMem_NEW(_sharedexception, 1);
205 if (err == NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800206 PyErr_NoMemory();
Eric Snow7f8bfc92018-01-29 18:23:44 -0700207 return NULL;
208 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800209 err->name = NULL;
210 err->msg = NULL;
211 return err;
212}
213
214static void
215_sharedexception_clear(_sharedexception *exc)
216{
217 if (exc->name != NULL) {
218 PyMem_Free(exc->name);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700219 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800220 if (exc->msg != NULL) {
221 PyMem_Free(exc->msg);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700222 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800223}
224
225static void
226_sharedexception_free(_sharedexception *exc)
227{
228 _sharedexception_clear(exc);
229 PyMem_Free(exc);
230}
231
232static _sharedexception *
233_sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb)
234{
235 assert(exctype != NULL);
236 char *failure = NULL;
237
238 _sharedexception *err = _sharedexception_new();
239 if (err == NULL) {
240 goto finally;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700241 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800242
243 PyObject *name = PyUnicode_FromFormat("%S", exctype);
244 if (name == NULL) {
245 failure = "unable to format exception type name";
246 goto finally;
247 }
248 err->name = _copy_raw_string(name);
249 Py_DECREF(name);
250 if (err->name == NULL) {
251 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
252 failure = "out of memory copying exception type name";
253 }
254 failure = "unable to encode and copy exception type name";
255 goto finally;
256 }
257
258 if (exc != NULL) {
259 PyObject *msg = PyUnicode_FromFormat("%S", exc);
260 if (msg == NULL) {
261 failure = "unable to format exception message";
262 goto finally;
263 }
264 err->msg = _copy_raw_string(msg);
265 Py_DECREF(msg);
266 if (err->msg == NULL) {
267 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
268 failure = "out of memory copying exception message";
269 }
270 failure = "unable to encode and copy exception message";
271 goto finally;
272 }
273 }
274
275finally:
276 if (failure != NULL) {
277 PyErr_Clear();
278 if (err->name != NULL) {
279 PyMem_Free(err->name);
280 err->name = NULL;
281 }
282 err->msg = failure;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700283 }
284 return err;
285}
286
Eric Snow7f8bfc92018-01-29 18:23:44 -0700287static void
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800288_sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700289{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800290 if (exc->name != NULL) {
291 if (exc->msg != NULL) {
292 PyErr_Format(wrapperclass, "%s: %s", exc->name, exc->msg);
293 }
294 else {
295 PyErr_SetString(wrapperclass, exc->name);
296 }
297 }
298 else if (exc->msg != NULL) {
299 PyErr_SetString(wrapperclass, exc->msg);
300 }
301 else {
302 PyErr_SetNone(wrapperclass);
303 }
Eric Snow7f8bfc92018-01-29 18:23:44 -0700304}
305
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800306
307/* channel-specific code ****************************************************/
Eric Snow7f8bfc92018-01-29 18:23:44 -0700308
309static PyObject *ChannelError;
310static PyObject *ChannelNotFoundError;
311static PyObject *ChannelClosedError;
312static PyObject *ChannelEmptyError;
313
314static int
315channel_exceptions_init(PyObject *ns)
316{
317 // XXX Move the exceptions into per-module memory?
318
319 // A channel-related operation failed.
320 ChannelError = PyErr_NewException("_xxsubinterpreters.ChannelError",
321 PyExc_RuntimeError, NULL);
322 if (ChannelError == NULL) {
323 return -1;
324 }
325 if (PyDict_SetItemString(ns, "ChannelError", ChannelError) != 0) {
326 return -1;
327 }
328
329 // An operation tried to use a channel that doesn't exist.
330 ChannelNotFoundError = PyErr_NewException(
331 "_xxsubinterpreters.ChannelNotFoundError", ChannelError, NULL);
332 if (ChannelNotFoundError == NULL) {
333 return -1;
334 }
335 if (PyDict_SetItemString(ns, "ChannelNotFoundError", ChannelNotFoundError) != 0) {
336 return -1;
337 }
338
339 // An operation tried to use a closed channel.
340 ChannelClosedError = PyErr_NewException(
341 "_xxsubinterpreters.ChannelClosedError", ChannelError, NULL);
342 if (ChannelClosedError == NULL) {
343 return -1;
344 }
345 if (PyDict_SetItemString(ns, "ChannelClosedError", ChannelClosedError) != 0) {
346 return -1;
347 }
348
349 // An operation tried to pop from an empty channel.
350 ChannelEmptyError = PyErr_NewException(
351 "_xxsubinterpreters.ChannelEmptyError", ChannelError, NULL);
352 if (ChannelEmptyError == NULL) {
353 return -1;
354 }
355 if (PyDict_SetItemString(ns, "ChannelEmptyError", ChannelEmptyError) != 0) {
356 return -1;
357 }
358
359 return 0;
360}
361
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800362/* the channel queue */
363
364struct _channelitem;
365
366typedef struct _channelitem {
367 _PyCrossInterpreterData *data;
368 struct _channelitem *next;
369} _channelitem;
370
371static _channelitem *
372_channelitem_new(void)
373{
374 _channelitem *item = PyMem_NEW(_channelitem, 1);
375 if (item == NULL) {
376 PyErr_NoMemory();
377 return NULL;
378 }
379 item->data = NULL;
380 item->next = NULL;
381 return item;
382}
383
384static void
385_channelitem_clear(_channelitem *item)
386{
387 if (item->data != NULL) {
388 _PyCrossInterpreterData_Release(item->data);
389 PyMem_Free(item->data);
390 item->data = NULL;
391 }
392 item->next = NULL;
393}
394
395static void
396_channelitem_free(_channelitem *item)
397{
398 _channelitem_clear(item);
399 PyMem_Free(item);
400}
401
402static void
403_channelitem_free_all(_channelitem *item)
404{
405 while (item != NULL) {
406 _channelitem *last = item;
407 item = item->next;
408 _channelitem_free(last);
409 }
410}
411
412static _PyCrossInterpreterData *
413_channelitem_popped(_channelitem *item)
414{
415 _PyCrossInterpreterData *data = item->data;
416 item->data = NULL;
417 _channelitem_free(item);
418 return data;
419}
420
421typedef struct _channelqueue {
422 int64_t count;
423 _channelitem *first;
424 _channelitem *last;
425} _channelqueue;
426
427static _channelqueue *
428_channelqueue_new(void)
429{
430 _channelqueue *queue = PyMem_NEW(_channelqueue, 1);
431 if (queue == NULL) {
432 PyErr_NoMemory();
433 return NULL;
434 }
435 queue->count = 0;
436 queue->first = NULL;
437 queue->last = NULL;
438 return queue;
439}
440
441static void
442_channelqueue_clear(_channelqueue *queue)
443{
444 _channelitem_free_all(queue->first);
445 queue->count = 0;
446 queue->first = NULL;
447 queue->last = NULL;
448}
449
450static void
451_channelqueue_free(_channelqueue *queue)
452{
453 _channelqueue_clear(queue);
454 PyMem_Free(queue);
455}
456
457static int
458_channelqueue_put(_channelqueue *queue, _PyCrossInterpreterData *data)
459{
460 _channelitem *item = _channelitem_new();
461 if (item == NULL) {
462 return -1;
463 }
464 item->data = data;
465
466 queue->count += 1;
467 if (queue->first == NULL) {
468 queue->first = item;
469 }
470 else {
471 queue->last->next = item;
472 }
473 queue->last = item;
474 return 0;
475}
476
477static _PyCrossInterpreterData *
478_channelqueue_get(_channelqueue *queue)
479{
480 _channelitem *item = queue->first;
481 if (item == NULL) {
482 return NULL;
483 }
484 queue->first = item->next;
485 if (queue->last == item) {
486 queue->last = NULL;
487 }
488 queue->count -= 1;
489
490 return _channelitem_popped(item);
491}
492
493/* channel-interpreter associations */
494
Eric Snow7f8bfc92018-01-29 18:23:44 -0700495struct _channelend;
496
497typedef struct _channelend {
498 struct _channelend *next;
499 int64_t interp;
500 int open;
501} _channelend;
502
503static _channelend *
504_channelend_new(int64_t interp)
505{
506 _channelend *end = PyMem_NEW(_channelend, 1);
507 if (end == NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800508 PyErr_NoMemory();
Eric Snow7f8bfc92018-01-29 18:23:44 -0700509 return NULL;
510 }
Eric Snow7f8bfc92018-01-29 18:23:44 -0700511 end->next = NULL;
512 end->interp = interp;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700513 end->open = 1;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700514 return end;
515}
516
517static void
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800518_channelend_free(_channelend *end)
519{
520 PyMem_Free(end);
521}
522
523static void
524_channelend_free_all(_channelend *end)
525{
Eric Snow7f8bfc92018-01-29 18:23:44 -0700526 while (end != NULL) {
527 _channelend *last = end;
528 end = end->next;
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800529 _channelend_free(last);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700530 }
531}
532
533static _channelend *
534_channelend_find(_channelend *first, int64_t interp, _channelend **pprev)
535{
536 _channelend *prev = NULL;
537 _channelend *end = first;
538 while (end != NULL) {
539 if (end->interp == interp) {
540 break;
541 }
542 prev = end;
543 end = end->next;
544 }
545 if (pprev != NULL) {
546 *pprev = prev;
547 }
548 return end;
549}
550
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800551typedef struct _channelassociations {
Eric Snow7f8bfc92018-01-29 18:23:44 -0700552 // Note that the list entries are never removed for interpreter
553 // for which the channel is closed. This should be a problem in
554 // practice. Also, a channel isn't automatically closed when an
555 // interpreter is destroyed.
556 int64_t numsendopen;
557 int64_t numrecvopen;
558 _channelend *send;
559 _channelend *recv;
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800560} _channelends;
561
562static _channelends *
563_channelends_new(void)
564{
565 _channelends *ends = PyMem_NEW(_channelends, 1);
566 if (ends== NULL) {
567 return NULL;
568 }
569 ends->numsendopen = 0;
570 ends->numrecvopen = 0;
571 ends->send = NULL;
572 ends->recv = NULL;
573 return ends;
574}
575
576static void
577_channelends_clear(_channelends *ends)
578{
579 _channelend_free_all(ends->send);
580 ends->send = NULL;
581 ends->numsendopen = 0;
582
583 _channelend_free_all(ends->recv);
584 ends->recv = NULL;
585 ends->numrecvopen = 0;
586}
587
588static void
589_channelends_free(_channelends *ends)
590{
591 _channelends_clear(ends);
592 PyMem_Free(ends);
593}
594
595static _channelend *
596_channelends_add(_channelends *ends, _channelend *prev, int64_t interp,
597 int send)
598{
599 _channelend *end = _channelend_new(interp);
600 if (end == NULL) {
601 return NULL;
602 }
603
604 if (prev == NULL) {
605 if (send) {
606 ends->send = end;
607 }
608 else {
609 ends->recv = end;
610 }
611 }
612 else {
613 prev->next = end;
614 }
615 if (send) {
616 ends->numsendopen += 1;
617 }
618 else {
619 ends->numrecvopen += 1;
620 }
621 return end;
622}
623
624static int
625_channelends_associate(_channelends *ends, int64_t interp, int send)
626{
627 _channelend *prev;
628 _channelend *end = _channelend_find(send ? ends->send : ends->recv,
629 interp, &prev);
630 if (end != NULL) {
631 if (!end->open) {
632 PyErr_SetString(ChannelClosedError, "channel already closed");
633 return -1;
634 }
635 // already associated
636 return 0;
637 }
638 if (_channelends_add(ends, prev, interp, send) == NULL) {
639 return -1;
640 }
641 return 0;
642}
643
644static int
645_channelends_is_open(_channelends *ends)
646{
647 if (ends->numsendopen != 0 || ends->numrecvopen != 0) {
648 return 1;
649 }
650 if (ends->send == NULL && ends->recv == NULL) {
651 return 1;
652 }
653 return 0;
654}
655
656static void
657_channelends_close_end(_channelends *ends, _channelend *end, int send)
658{
659 end->open = 0;
660 if (send) {
661 ends->numsendopen -= 1;
662 }
663 else {
664 ends->numrecvopen -= 1;
665 }
666}
667
668static int
669_channelends_close_interpreter(_channelends *ends, int64_t interp, int which)
670{
671 _channelend *prev;
672 _channelend *end;
673 if (which >= 0) { // send/both
674 end = _channelend_find(ends->send, interp, &prev);
675 if (end == NULL) {
676 // never associated so add it
677 end = _channelends_add(ends, prev, interp, 1);
678 if (end == NULL) {
679 return -1;
680 }
681 }
682 _channelends_close_end(ends, end, 1);
683 }
684 if (which <= 0) { // recv/both
685 end = _channelend_find(ends->recv, interp, &prev);
686 if (end == NULL) {
687 // never associated so add it
688 end = _channelends_add(ends, prev, interp, 0);
689 if (end == NULL) {
690 return -1;
691 }
692 }
693 _channelends_close_end(ends, end, 0);
694 }
695 return 0;
696}
697
698static void
699_channelends_close_all(_channelends *ends)
700{
701 // Ensure all the "send"-associated interpreters are closed.
702 _channelend *end;
703 for (end = ends->send; end != NULL; end = end->next) {
704 _channelends_close_end(ends, end, 1);
705 }
706
707 // Ensure all the "recv"-associated interpreters are closed.
708 for (end = ends->recv; end != NULL; end = end->next) {
709 _channelends_close_end(ends, end, 0);
710 }
711}
712
713/* channels */
714
715struct _channel;
716
717typedef struct _channel {
718 PyThread_type_lock mutex;
719 _channelqueue *queue;
720 _channelends *ends;
721 int open;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700722} _PyChannelState;
723
724static _PyChannelState *
725_channel_new(void)
726{
727 _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1);
728 if (chan == NULL) {
729 return NULL;
730 }
731 chan->mutex = PyThread_allocate_lock();
732 if (chan->mutex == NULL) {
733 PyMem_Free(chan);
734 PyErr_SetString(ChannelError,
735 "can't initialize mutex for new channel");
736 return NULL;
737 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800738 chan->queue = _channelqueue_new();
739 if (chan->queue == NULL) {
740 PyMem_Free(chan);
741 return NULL;
742 }
743 chan->ends = _channelends_new();
744 if (chan->ends == NULL) {
745 _channelqueue_free(chan->queue);
746 PyMem_Free(chan);
747 return NULL;
748 }
Eric Snow7f8bfc92018-01-29 18:23:44 -0700749 chan->open = 1;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700750 return chan;
751}
752
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800753static void
754_channel_free(_PyChannelState *chan)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700755{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800756 PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
757 _channelqueue_free(chan->queue);
758 _channelends_free(chan->ends);
759 PyThread_release_lock(chan->mutex);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700760
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800761 PyThread_free_lock(chan->mutex);
762 PyMem_Free(chan);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700763}
764
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800765static int
766_channel_add(_PyChannelState *chan, int64_t interp,
767 _PyCrossInterpreterData *data)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700768{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800769 int res = -1;
770 PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
771
Eric Snow7f8bfc92018-01-29 18:23:44 -0700772 if (!chan->open) {
773 PyErr_SetString(ChannelClosedError, "channel closed");
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800774 goto done;
775 }
776 if (_channelends_associate(chan->ends, interp, 1) != 0) {
777 goto done;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700778 }
779
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800780 if (_channelqueue_put(chan->queue, data) != 0) {
781 goto done;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700782 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800783
784 res = 0;
785done:
786 PyThread_release_lock(chan->mutex);
787 return res;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700788}
789
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800790static _PyCrossInterpreterData *
791_channel_next(_PyChannelState *chan, int64_t interp)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700792{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800793 _PyCrossInterpreterData *data = NULL;
794 PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
795
796 if (!chan->open) {
797 PyErr_SetString(ChannelClosedError, "channel closed");
798 goto done;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700799 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800800 if (_channelends_associate(chan->ends, interp, 0) != 0) {
801 goto done;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700802 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800803
804 data = _channelqueue_get(chan->queue);
805done:
806 PyThread_release_lock(chan->mutex);
807 return data;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700808}
809
810static int
811_channel_close_interpreter(_PyChannelState *chan, int64_t interp, int which)
812{
813 PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
814
815 int res = -1;
816 if (!chan->open) {
817 PyErr_SetString(ChannelClosedError, "channel already closed");
818 goto done;
819 }
820
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800821 if (_channelends_close_interpreter(chan->ends, interp, which) != 0) {
822 goto done;
Eric Snow7f8bfc92018-01-29 18:23:44 -0700823 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800824 chan->open = _channelends_is_open(chan->ends);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700825
826 res = 0;
827done:
828 PyThread_release_lock(chan->mutex);
829 return res;
830}
831
832static int
833_channel_close_all(_PyChannelState *chan)
834{
835 int res = -1;
836 PyThread_acquire_lock(chan->mutex, WAIT_LOCK);
837
838 if (!chan->open) {
839 PyErr_SetString(ChannelClosedError, "channel already closed");
840 goto done;
841 }
842
843 chan->open = 0;
844
845 // We *could* also just leave these in place, since we've marked
846 // the channel as closed already.
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800847 _channelends_close_all(chan->ends);
Eric Snow7f8bfc92018-01-29 18:23:44 -0700848
849 res = 0;
850done:
851 PyThread_release_lock(chan->mutex);
852 return res;
853}
854
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800855/* the set of channels */
Eric Snow7f8bfc92018-01-29 18:23:44 -0700856
857struct _channelref;
858
859typedef struct _channelref {
860 int64_t id;
861 _PyChannelState *chan;
862 struct _channelref *next;
863 Py_ssize_t objcount;
864} _channelref;
865
866static _channelref *
867_channelref_new(int64_t id, _PyChannelState *chan)
868{
869 _channelref *ref = PyMem_NEW(_channelref, 1);
870 if (ref == NULL) {
871 return NULL;
872 }
873 ref->id = id;
874 ref->chan = chan;
875 ref->next = NULL;
876 ref->objcount = 0;
877 return ref;
878}
879
Miss Islington (bot)f33eced2018-02-02 21:38:57 -0800880//static void
881//_channelref_clear(_channelref *ref)
882//{
883// ref->id = -1;
884// ref->chan = NULL;
885// ref->next = NULL;
886// ref->objcount = 0;
887//}
888
889static void
890_channelref_free(_channelref *ref)
891{
892 //_channelref_clear(ref);
893 PyMem_Free(ref);
894}
895
Eric Snow7f8bfc92018-01-29 18:23:44 -0700896static _channelref *
897_channelref_find(_channelref *first, int64_t id, _channelref **pprev)
898{
899 _channelref *prev = NULL;
900 _channelref *ref = first;
901 while (ref != NULL) {
902 if (ref->id == id) {
903 break;
904 }
905 prev = ref;
906 ref = ref->next;
907 }
908 if (pprev != NULL) {
909 *pprev = prev;
910 }
911 return ref;
912}
913
914typedef struct _channels {
915 PyThread_type_lock mutex;
916 _channelref *head;
917 int64_t numopen;
918 int64_t next_id;
919} _channels;
920
921static int
922_channels_init(_channels *channels)
923{
924 if (channels->mutex == NULL) {
925 channels->mutex = PyThread_allocate_lock();
926 if (channels->mutex == NULL) {
Eric Snow7f8bfc92018-01-29 18:23:44 -0700927 PyErr_SetString(ChannelError,
928 "can't initialize mutex for channel management");
929 return -1;
930 }
931 }
932 channels->head = NULL;
933 channels->numopen = 0;
934 channels->next_id = 0;
935 return 0;
936}
937
938static int64_t
939_channels_next_id(_channels *channels) // needs lock
940{
941 int64_t id = channels->next_id;
942 if (id < 0) {
943 /* overflow */
944 PyErr_SetString(ChannelError,
945 "failed to get a channel ID");
946 return -1;
947 }
948 channels->next_id += 1;
949 return id;
950}
951
952static _PyChannelState *
953_channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex)
954{
955 _PyChannelState *chan = NULL;
956 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
957 if (pmutex != NULL) {
958 *pmutex = NULL;
959 }
960
961 _channelref *ref = _channelref_find(channels->head, id, NULL);
962 if (ref == NULL) {
963 PyErr_Format(ChannelNotFoundError, "channel %d not found", id);
964 goto done;
965 }
966 if (ref->chan == NULL || !ref->chan->open) {
967 PyErr_Format(ChannelClosedError, "channel %d closed", id);
968 goto done;
969 }
970
971 if (pmutex != NULL) {
972 // The mutex will be closed by the caller.
973 *pmutex = channels->mutex;
974 }
975
976 chan = ref->chan;
977done:
978 if (pmutex == NULL || *pmutex == NULL) {
979 PyThread_release_lock(channels->mutex);
980 }
981 return chan;
982}
983
984static int64_t
985_channels_add(_channels *channels, _PyChannelState *chan)
986{
987 int64_t cid = -1;
988 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
989
990 // Create a new ref.
991 int64_t id = _channels_next_id(channels);
992 if (id < 0) {
993 goto done;
994 }
995 _channelref *ref = _channelref_new(id, chan);
996 if (ref == NULL) {
997 goto done;
998 }
999
1000 // Add it to the list.
1001 // We assume that the channel is a new one (not already in the list).
1002 ref->next = channels->head;
1003 channels->head = ref;
1004 channels->numopen += 1;
1005
1006 cid = id;
1007done:
1008 PyThread_release_lock(channels->mutex);
1009 return cid;
1010}
1011
1012static int
1013_channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan)
1014{
1015 int res = -1;
1016 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
1017 if (pchan != NULL) {
1018 *pchan = NULL;
1019 }
1020
1021 _channelref *ref = _channelref_find(channels->head, cid, NULL);
1022 if (ref == NULL) {
1023 PyErr_Format(ChannelNotFoundError, "channel %d not found", cid);
1024 goto done;
1025 }
1026
1027 if (ref->chan == NULL) {
1028 PyErr_Format(ChannelClosedError, "channel %d closed", cid);
1029 goto done;
1030 }
1031 else {
1032 if (_channel_close_all(ref->chan) != 0) {
1033 goto done;
1034 }
1035 if (pchan != NULL) {
1036 *pchan = ref->chan;
1037 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001038 else {
1039 _channel_free(ref->chan);
1040 }
Eric Snow7f8bfc92018-01-29 18:23:44 -07001041 ref->chan = NULL;
1042 }
1043
1044 res = 0;
1045done:
1046 PyThread_release_lock(channels->mutex);
1047 return res;
1048}
1049
1050static void
1051_channels_remove_ref(_channels *channels, _channelref *ref, _channelref *prev,
1052 _PyChannelState **pchan)
1053{
1054 if (ref == channels->head) {
1055 channels->head = ref->next;
1056 }
1057 else {
1058 prev->next = ref->next;
1059 }
1060 channels->numopen -= 1;
1061
1062 if (pchan != NULL) {
1063 *pchan = ref->chan;
1064 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001065 _channelref_free(ref);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001066}
1067
1068static int
1069_channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan)
1070{
1071 int res = -1;
1072 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
1073
1074 if (pchan != NULL) {
1075 *pchan = NULL;
1076 }
1077
1078 _channelref *prev = NULL;
1079 _channelref *ref = _channelref_find(channels->head, id, &prev);
1080 if (ref == NULL) {
1081 PyErr_Format(ChannelNotFoundError, "channel %d not found", id);
1082 goto done;
1083 }
1084
1085 _channels_remove_ref(channels, ref, prev, pchan);
1086
1087 res = 0;
1088done:
1089 PyThread_release_lock(channels->mutex);
1090 return res;
1091}
1092
1093static int
1094_channels_add_id_object(_channels *channels, int64_t id)
1095{
1096 int res = -1;
1097 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
1098
1099 _channelref *ref = _channelref_find(channels->head, id, NULL);
1100 if (ref == NULL) {
1101 PyErr_Format(ChannelNotFoundError, "channel %d not found", id);
1102 goto done;
1103 }
1104 ref->objcount += 1;
1105
1106 res = 0;
1107done:
1108 PyThread_release_lock(channels->mutex);
1109 return res;
1110}
1111
1112static void
1113_channels_drop_id_object(_channels *channels, int64_t id)
1114{
1115 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
1116
1117 _channelref *prev = NULL;
1118 _channelref *ref = _channelref_find(channels->head, id, &prev);
1119 if (ref == NULL) {
1120 // Already destroyed.
1121 goto done;
1122 }
1123 ref->objcount -= 1;
1124
1125 // Destroy if no longer used.
1126 if (ref->objcount == 0) {
1127 _PyChannelState *chan = NULL;
1128 _channels_remove_ref(channels, ref, prev, &chan);
1129 if (chan != NULL) {
1130 _channel_free(chan);
1131 }
1132 }
1133
1134done:
1135 PyThread_release_lock(channels->mutex);
1136}
1137
1138int64_t *
1139_channels_list_all(_channels *channels, int64_t *count)
1140{
1141 int64_t *cids = NULL;
1142 PyThread_acquire_lock(channels->mutex, WAIT_LOCK);
1143 int64_t numopen = channels->numopen;
1144 if (numopen >= PY_SSIZE_T_MAX) {
1145 PyErr_SetString(PyExc_RuntimeError, "too many channels open");
1146 goto done;
1147 }
1148 int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen));
1149 if (ids == NULL) {
1150 goto done;
1151 }
1152 _channelref *ref = channels->head;
1153 for (int64_t i=0; ref != NULL; ref = ref->next, i++) {
1154 ids[i] = ref->id;
1155 }
1156 *count = channels->numopen;
1157
1158 cids = ids;
1159done:
1160 PyThread_release_lock(channels->mutex);
1161 return cids;
1162}
1163
1164/* "high"-level channel-related functions */
1165
1166static int64_t
1167_channel_create(_channels *channels)
1168{
1169 _PyChannelState *chan = _channel_new();
1170 if (chan == NULL) {
1171 return -1;
1172 }
1173 int64_t id = _channels_add(channels, chan);
1174 if (id < 0) {
1175 _channel_free(chan);
1176 return -1;
1177 }
1178 return id;
1179}
1180
1181static int
1182_channel_destroy(_channels *channels, int64_t id)
1183{
1184 _PyChannelState *chan = NULL;
1185 if (_channels_remove(channels, id, &chan) != 0) {
1186 return -1;
1187 }
1188 if (chan != NULL) {
1189 _channel_free(chan);
1190 }
1191 return 0;
1192}
1193
1194static int
1195_channel_send(_channels *channels, int64_t id, PyObject *obj)
1196{
1197 PyInterpreterState *interp = _get_current();
1198 if (interp == NULL) {
1199 return -1;
1200 }
1201
1202 // Look up the channel.
1203 PyThread_type_lock mutex = NULL;
1204 _PyChannelState *chan = _channels_lookup(channels, id, &mutex);
1205 if (chan == NULL) {
1206 return -1;
1207 }
1208 // Past this point we are responsible for releasing the mutex.
1209
1210 // Convert the object to cross-interpreter data.
1211 _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1);
1212 if (data == NULL) {
1213 PyThread_release_lock(mutex);
1214 return -1;
1215 }
1216 if (_PyObject_GetCrossInterpreterData(obj, data) != 0) {
1217 PyThread_release_lock(mutex);
1218 return -1;
1219 }
1220
1221 // Add the data to the channel.
1222 int res = _channel_add(chan, interp->id, data);
1223 PyThread_release_lock(mutex);
1224 if (res != 0) {
1225 _PyCrossInterpreterData_Release(data);
1226 PyMem_Free(data);
1227 return -1;
1228 }
1229
1230 return 0;
1231}
1232
1233static PyObject *
1234_channel_recv(_channels *channels, int64_t id)
1235{
1236 PyInterpreterState *interp = _get_current();
1237 if (interp == NULL) {
1238 return NULL;
1239 }
1240
1241 // Look up the channel.
1242 PyThread_type_lock mutex = NULL;
1243 _PyChannelState *chan = _channels_lookup(channels, id, &mutex);
1244 if (chan == NULL) {
1245 return NULL;
1246 }
1247 // Past this point we are responsible for releasing the mutex.
1248
1249 // Pop off the next item from the channel.
1250 _PyCrossInterpreterData *data = _channel_next(chan, interp->id);
1251 PyThread_release_lock(mutex);
1252 if (data == NULL) {
1253 PyErr_Format(ChannelEmptyError, "channel %d is empty", id);
1254 return NULL;
1255 }
1256
1257 // Convert the data back to an object.
1258 PyObject *obj = _PyCrossInterpreterData_NewObject(data);
1259 if (obj == NULL) {
1260 return NULL;
1261 }
1262 _PyCrossInterpreterData_Release(data);
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001263 PyMem_Free(data);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001264
1265 return obj;
1266}
1267
1268static int
1269_channel_drop(_channels *channels, int64_t id, int send, int recv)
1270{
1271 PyInterpreterState *interp = _get_current();
1272 if (interp == NULL) {
1273 return -1;
1274 }
1275
1276 // Look up the channel.
1277 PyThread_type_lock mutex = NULL;
1278 _PyChannelState *chan = _channels_lookup(channels, id, &mutex);
1279 if (chan == NULL) {
1280 return -1;
1281 }
1282 // Past this point we are responsible for releasing the mutex.
1283
1284 // Close one or both of the two ends.
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001285 int res = _channel_close_interpreter(chan, interp->id, send-recv);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001286 PyThread_release_lock(mutex);
1287 return res;
1288}
1289
1290static int
1291_channel_close(_channels *channels, int64_t id)
1292{
1293 return _channels_close(channels, id, NULL);
1294}
1295
1296/* ChannelID class */
1297
1298#define CHANNEL_SEND 1
1299#define CHANNEL_RECV -1
1300
1301static PyTypeObject ChannelIDtype;
1302
1303typedef struct channelid {
1304 PyObject_HEAD
1305 int64_t id;
1306 int end;
1307 _channels *channels;
1308} channelid;
1309
1310static channelid *
1311newchannelid(PyTypeObject *cls, int64_t cid, int end, _channels *channels,
1312 int force)
1313{
1314 channelid *self = PyObject_New(channelid, cls);
1315 if (self == NULL) {
1316 return NULL;
1317 }
1318 self->id = cid;
1319 self->end = end;
1320 self->channels = channels;
1321
1322 if (_channels_add_id_object(channels, cid) != 0) {
1323 if (force && PyErr_ExceptionMatches(ChannelNotFoundError)) {
1324 PyErr_Clear();
1325 }
1326 else {
1327 Py_DECREF((PyObject *)self);
1328 return NULL;
1329 }
1330 }
1331
1332 return self;
1333}
1334
1335static _channels * _global_channels(void);
1336
1337static PyObject *
1338channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds)
1339{
1340 static char *kwlist[] = {"id", "send", "recv", "force", NULL};
1341 PyObject *id;
1342 int send = -1;
1343 int recv = -1;
1344 int force = 0;
1345 if (!PyArg_ParseTupleAndKeywords(args, kwds,
1346 "O|$ppp:ChannelID.__init__", kwlist,
1347 &id, &send, &recv, &force))
1348 return NULL;
1349
1350 // Coerce and check the ID.
1351 int64_t cid;
1352 if (PyObject_TypeCheck(id, &ChannelIDtype)) {
1353 cid = ((channelid *)id)->id;
1354 }
1355 else {
1356 cid = _coerce_id(id);
1357 if (cid < 0) {
1358 return NULL;
1359 }
1360 }
1361
1362 // Handle "send" and "recv".
1363 if (send == 0 && recv == 0) {
1364 PyErr_SetString(PyExc_ValueError,
1365 "'send' and 'recv' cannot both be False");
1366 return NULL;
1367 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001368
Eric Snow7f8bfc92018-01-29 18:23:44 -07001369 int end = 0;
1370 if (send == 1) {
1371 if (recv == 0 || recv == -1) {
1372 end = CHANNEL_SEND;
1373 }
1374 }
1375 else if (recv == 1) {
1376 end = CHANNEL_RECV;
1377 }
1378
1379 return (PyObject *)newchannelid(cls, cid, end, _global_channels(), force);
1380}
1381
1382static void
1383channelid_dealloc(PyObject *v)
1384{
1385 int64_t cid = ((channelid *)v)->id;
1386 _channels *channels = ((channelid *)v)->channels;
1387 Py_TYPE(v)->tp_free(v);
1388
1389 _channels_drop_id_object(channels, cid);
1390}
1391
1392static PyObject *
1393channelid_repr(PyObject *self)
1394{
1395 PyTypeObject *type = Py_TYPE(self);
1396 const char *name = _PyType_Name(type);
1397
1398 channelid *cid = (channelid *)self;
1399 const char *fmt;
1400 if (cid->end == CHANNEL_SEND) {
1401 fmt = "%s(%d, send=True)";
1402 }
1403 else if (cid->end == CHANNEL_RECV) {
1404 fmt = "%s(%d, recv=True)";
1405 }
1406 else {
1407 fmt = "%s(%d)";
1408 }
1409 return PyUnicode_FromFormat(fmt, name, cid->id);
1410}
1411
1412PyObject *
1413channelid_int(PyObject *self)
1414{
1415 channelid *cid = (channelid *)self;
1416 return PyLong_FromLongLong(cid->id);
1417}
1418
1419static PyNumberMethods channelid_as_number = {
1420 0, /* nb_add */
1421 0, /* nb_subtract */
1422 0, /* nb_multiply */
1423 0, /* nb_remainder */
1424 0, /* nb_divmod */
1425 0, /* nb_power */
1426 0, /* nb_negative */
1427 0, /* nb_positive */
1428 0, /* nb_absolute */
1429 0, /* nb_bool */
1430 0, /* nb_invert */
1431 0, /* nb_lshift */
1432 0, /* nb_rshift */
1433 0, /* nb_and */
1434 0, /* nb_xor */
1435 0, /* nb_or */
1436 (unaryfunc)channelid_int, /* nb_int */
1437 0, /* nb_reserved */
1438 0, /* nb_float */
1439
1440 0, /* nb_inplace_add */
1441 0, /* nb_inplace_subtract */
1442 0, /* nb_inplace_multiply */
1443 0, /* nb_inplace_remainder */
1444 0, /* nb_inplace_power */
1445 0, /* nb_inplace_lshift */
1446 0, /* nb_inplace_rshift */
1447 0, /* nb_inplace_and */
1448 0, /* nb_inplace_xor */
1449 0, /* nb_inplace_or */
1450
1451 0, /* nb_floor_divide */
1452 0, /* nb_true_divide */
1453 0, /* nb_inplace_floor_divide */
1454 0, /* nb_inplace_true_divide */
1455
1456 (unaryfunc)channelid_int, /* nb_index */
1457};
1458
1459static Py_hash_t
1460channelid_hash(PyObject *self)
1461{
1462 channelid *cid = (channelid *)self;
1463 PyObject *id = PyLong_FromLongLong(cid->id);
1464 if (id == NULL) {
1465 return -1;
1466 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001467 Py_hash_t hash = PyObject_Hash(id);
1468 Py_DECREF(id);
1469 return hash;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001470}
1471
1472static PyObject *
1473channelid_richcompare(PyObject *self, PyObject *other, int op)
1474{
1475 if (op != Py_EQ && op != Py_NE) {
1476 Py_RETURN_NOTIMPLEMENTED;
1477 }
1478
1479 if (!PyObject_TypeCheck(self, &ChannelIDtype)) {
1480 Py_RETURN_NOTIMPLEMENTED;
1481 }
1482
1483 channelid *cid = (channelid *)self;
1484 int equal;
1485 if (PyObject_TypeCheck(other, &ChannelIDtype)) {
1486 channelid *othercid = (channelid *)other;
1487 if (cid->end != othercid->end) {
1488 equal = 0;
1489 }
1490 else {
1491 equal = (cid->id == othercid->id);
1492 }
1493 }
1494 else {
1495 other = PyNumber_Long(other);
1496 if (other == NULL) {
1497 PyErr_Clear();
1498 Py_RETURN_NOTIMPLEMENTED;
1499 }
1500 int64_t othercid = PyLong_AsLongLong(other);
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001501 Py_DECREF(other);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001502 if (othercid == -1 && PyErr_Occurred() != NULL) {
1503 return NULL;
1504 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001505 if (othercid < 0) {
Eric Snow7f8bfc92018-01-29 18:23:44 -07001506 equal = 0;
1507 }
1508 else {
1509 equal = (cid->id == othercid);
1510 }
1511 }
1512
1513 if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) {
1514 Py_RETURN_TRUE;
1515 }
1516 Py_RETURN_FALSE;
1517}
1518
1519struct _channelid_xid {
1520 int64_t id;
1521 int end;
1522};
1523
1524static PyObject *
1525_channelid_from_xid(_PyCrossInterpreterData *data)
1526{
1527 struct _channelid_xid *xid = (struct _channelid_xid *)data->data;
1528 return (PyObject *)newchannelid(&ChannelIDtype, xid->id, xid->end,
1529 _global_channels(), 0);
1530}
1531
1532static int
1533_channelid_shared(PyObject *obj, _PyCrossInterpreterData *data)
1534{
1535 struct _channelid_xid *xid = PyMem_NEW(struct _channelid_xid, 1);
1536 if (xid == NULL) {
1537 return -1;
1538 }
1539 xid->id = ((channelid *)obj)->id;
1540 xid->end = ((channelid *)obj)->end;
1541
1542 data->data = xid;
1543 data->obj = obj;
1544 data->new_object = _channelid_from_xid;
1545 data->free = PyMem_Free;
1546 return 0;
1547}
1548
1549static PyObject *
1550channelid_end(PyObject *self, void *end)
1551{
1552 int force = 1;
1553 channelid *cid = (channelid *)self;
1554 if (end != NULL) {
1555 return (PyObject *)newchannelid(Py_TYPE(self), cid->id, *(int *)end,
1556 cid->channels, force);
1557 }
1558
1559 if (cid->end == CHANNEL_SEND) {
1560 return PyUnicode_InternFromString("send");
1561 }
1562 if (cid->end == CHANNEL_RECV) {
1563 return PyUnicode_InternFromString("recv");
1564 }
1565 return PyUnicode_InternFromString("both");
1566}
1567
1568static int _channelid_end_send = CHANNEL_SEND;
1569static int _channelid_end_recv = CHANNEL_RECV;
1570
1571static PyGetSetDef channelid_getsets[] = {
1572 {"end", (getter)channelid_end, NULL,
1573 PyDoc_STR("'send', 'recv', or 'both'")},
1574 {"send", (getter)channelid_end, NULL,
1575 PyDoc_STR("the 'send' end of the channel"), &_channelid_end_send},
1576 {"recv", (getter)channelid_end, NULL,
1577 PyDoc_STR("the 'recv' end of the channel"), &_channelid_end_recv},
1578 {NULL}
1579};
1580
1581PyDoc_STRVAR(channelid_doc,
1582"A channel ID identifies a channel and may be used as an int.");
1583
1584static PyTypeObject ChannelIDtype = {
1585 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1586 "_xxsubinterpreters.ChannelID", /* tp_name */
1587 sizeof(channelid), /* tp_size */
1588 0, /* tp_itemsize */
1589 (destructor)channelid_dealloc, /* tp_dealloc */
1590 0, /* tp_print */
1591 0, /* tp_getattr */
1592 0, /* tp_setattr */
1593 0, /* tp_as_async */
1594 (reprfunc)channelid_repr, /* tp_repr */
1595 &channelid_as_number, /* tp_as_number */
1596 0, /* tp_as_sequence */
1597 0, /* tp_as_mapping */
1598 channelid_hash, /* tp_hash */
1599 0, /* tp_call */
1600 0, /* tp_str */
1601 0, /* tp_getattro */
1602 0, /* tp_setattro */
1603 0, /* tp_as_buffer */
1604 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
1605 Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */
1606 channelid_doc, /* tp_doc */
1607 0, /* tp_traverse */
1608 0, /* tp_clear */
1609 channelid_richcompare, /* tp_richcompare */
1610 0, /* tp_weaklistoffset */
1611 0, /* tp_iter */
1612 0, /* tp_iternext */
1613 0, /* tp_methods */
1614 0, /* tp_members */
1615 channelid_getsets, /* tp_getset */
1616 0, /* tp_base */
1617 0, /* tp_dict */
1618 0, /* tp_descr_get */
1619 0, /* tp_descr_set */
1620 0, /* tp_dictoffset */
1621 0, /* tp_init */
1622 0, /* tp_alloc */
1623 // Note that we do not set tp_new to channelid_new. Instead we
1624 // set it to NULL, meaning it cannot be instantiated from Python
1625 // code. We do this because there is a strong relationship between
1626 // channel IDs and the channel lifecycle, so this limitation avoids
1627 // related complications.
1628 NULL, /* tp_new */
1629};
1630
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001631
1632/* interpreter-specific code ************************************************/
1633
1634static PyObject * RunFailedError = NULL;
1635
1636static int
1637interp_exceptions_init(PyObject *ns)
1638{
1639 // XXX Move the exceptions into per-module memory?
1640
1641 if (RunFailedError == NULL) {
1642 // An uncaught exception came out of interp_run_string().
1643 RunFailedError = PyErr_NewException("_xxsubinterpreters.RunFailedError",
1644 PyExc_RuntimeError, NULL);
1645 if (RunFailedError == NULL) {
1646 return -1;
1647 }
1648 if (PyDict_SetItemString(ns, "RunFailedError", RunFailedError) != 0) {
1649 return -1;
1650 }
1651 }
1652
1653 return 0;
1654}
Eric Snow7f8bfc92018-01-29 18:23:44 -07001655
Eric Snow7f8bfc92018-01-29 18:23:44 -07001656static int
1657_is_running(PyInterpreterState *interp)
1658{
1659 PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1660 if (PyThreadState_Next(tstate) != NULL) {
1661 PyErr_SetString(PyExc_RuntimeError,
1662 "interpreter has more than one thread");
1663 return -1;
1664 }
1665 PyFrameObject *frame = tstate->frame;
1666 if (frame == NULL) {
1667 if (PyErr_Occurred() != NULL) {
1668 return -1;
1669 }
1670 return 0;
1671 }
1672 return (int)(frame->f_executing);
1673}
1674
1675static int
1676_ensure_not_running(PyInterpreterState *interp)
1677{
1678 int is_running = _is_running(interp);
1679 if (is_running < 0) {
1680 return -1;
1681 }
1682 if (is_running) {
1683 PyErr_Format(PyExc_RuntimeError, "interpreter already running");
1684 return -1;
1685 }
1686 return 0;
1687}
1688
1689static int
1690_run_script(PyInterpreterState *interp, const char *codestr,
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001691 _sharedns *shared, _sharedexception **exc)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001692{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001693 PyObject *exctype = NULL;
1694 PyObject *excval = NULL;
1695 PyObject *tb = NULL;
1696
Eric Snow7f8bfc92018-01-29 18:23:44 -07001697 PyObject *main_mod = PyMapping_GetItemString(interp->modules, "__main__");
1698 if (main_mod == NULL) {
1699 goto error;
1700 }
1701 PyObject *ns = PyModule_GetDict(main_mod); // borrowed
1702 Py_DECREF(main_mod);
1703 if (ns == NULL) {
1704 goto error;
1705 }
1706 Py_INCREF(ns);
1707
1708 // Apply the cross-interpreter data.
1709 if (shared != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001710 if (_sharedns_apply(shared, ns) != 0) {
1711 Py_DECREF(ns);
1712 goto error;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001713 }
1714 }
1715
1716 // Run the string (see PyRun_SimpleStringFlags).
1717 PyObject *result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL);
1718 Py_DECREF(ns);
1719 if (result == NULL) {
1720 goto error;
1721 }
1722 else {
1723 Py_DECREF(result); // We throw away the result.
1724 }
1725
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001726 *exc = NULL;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001727 return 0;
1728
1729error:
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001730 PyErr_Fetch(&exctype, &excval, &tb);
1731
1732 _sharedexception *sharedexc = _sharedexception_bind(exctype, excval, tb);
1733 Py_XDECREF(exctype);
1734 Py_XDECREF(excval);
1735 Py_XDECREF(tb);
1736 if (sharedexc == NULL) {
1737 fprintf(stderr, "RunFailedError: script raised an uncaught exception");
1738 PyErr_Clear();
1739 sharedexc = NULL;
1740 }
1741 else {
1742 assert(!PyErr_Occurred());
1743 }
1744 *exc = sharedexc;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001745 return -1;
1746}
1747
1748static int
1749_run_script_in_interpreter(PyInterpreterState *interp, const char *codestr,
1750 PyObject *shareables)
1751{
1752 if (_ensure_not_running(interp) < 0) {
1753 return -1;
1754 }
1755
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001756 _sharedns *shared = _get_shared_ns(shareables);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001757 if (shared == NULL && PyErr_Occurred()) {
1758 return -1;
1759 }
1760
1761 // Switch to interpreter.
Miss Islington (bot)eed3c7a2018-02-20 16:09:41 -08001762 PyThreadState *save_tstate = NULL;
1763 if (interp != PyThreadState_Get()->interp) {
1764 // XXX Using the "head" thread isn't strictly correct.
1765 PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1766 // XXX Possible GILState issues?
1767 save_tstate = PyThreadState_Swap(tstate);
1768 }
Eric Snow7f8bfc92018-01-29 18:23:44 -07001769
1770 // Run the script.
1771 _sharedexception *exc = NULL;
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001772 int result = _run_script(interp, codestr, shared, &exc);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001773
1774 // Switch back.
1775 if (save_tstate != NULL) {
1776 PyThreadState_Swap(save_tstate);
1777 }
1778
1779 // Propagate any exception out to the caller.
1780 if (exc != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001781 _sharedexception_apply(exc, RunFailedError);
1782 _sharedexception_free(exc);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001783 }
1784 else if (result != 0) {
1785 // We were unable to allocate a shared exception.
1786 PyErr_NoMemory();
1787 }
1788
1789 if (shared != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001790 _sharedns_free(shared);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001791 }
1792
1793 return result;
1794}
1795
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08001796/* InterpreterID class */
1797
1798static PyTypeObject InterpreterIDtype;
1799
1800typedef struct interpid {
1801 PyObject_HEAD
1802 int64_t id;
1803} interpid;
1804
1805static interpid *
1806newinterpid(PyTypeObject *cls, int64_t id, int force)
1807{
1808 PyInterpreterState *interp = _PyInterpreterState_LookUpID(id);
1809 if (interp == NULL) {
1810 if (force) {
1811 PyErr_Clear();
1812 }
1813 else {
1814 return NULL;
1815 }
1816 }
1817
1818 interpid *self = PyObject_New(interpid, cls);
1819 if (self == NULL) {
1820 return NULL;
1821 }
1822 self->id = id;
1823
1824 if (interp != NULL) {
1825 _PyInterpreterState_IDIncref(interp);
1826 }
1827 return self;
1828}
1829
1830static PyObject *
1831interpid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds)
1832{
1833 static char *kwlist[] = {"id", "force", NULL};
1834 PyObject *idobj;
1835 int force = 0;
1836 if (!PyArg_ParseTupleAndKeywords(args, kwds,
1837 "O|$p:InterpreterID.__init__", kwlist,
1838 &idobj, &force)) {
1839 return NULL;
1840 }
1841
1842 // Coerce and check the ID.
1843 int64_t id;
1844 if (PyObject_TypeCheck(idobj, &InterpreterIDtype)) {
1845 id = ((interpid *)idobj)->id;
1846 }
1847 else {
1848 id = _coerce_id(idobj);
1849 if (id < 0) {
1850 return NULL;
1851 }
1852 }
1853
1854 return (PyObject *)newinterpid(cls, id, force);
1855}
1856
1857static void
1858interpid_dealloc(PyObject *v)
1859{
1860 int64_t id = ((interpid *)v)->id;
1861 PyInterpreterState *interp = _PyInterpreterState_LookUpID(id);
1862 if (interp != NULL) {
1863 _PyInterpreterState_IDDecref(interp);
1864 }
1865 else {
1866 // already deleted
1867 PyErr_Clear();
1868 }
1869 Py_TYPE(v)->tp_free(v);
1870}
1871
1872static PyObject *
1873interpid_repr(PyObject *self)
1874{
1875 PyTypeObject *type = Py_TYPE(self);
1876 const char *name = _PyType_Name(type);
1877 interpid *id = (interpid *)self;
1878 return PyUnicode_FromFormat("%s(%d)", name, id->id);
1879}
1880
1881PyObject *
1882interpid_int(PyObject *self)
1883{
1884 interpid *id = (interpid *)self;
1885 return PyLong_FromLongLong(id->id);
1886}
1887
1888static PyNumberMethods interpid_as_number = {
1889 0, /* nb_add */
1890 0, /* nb_subtract */
1891 0, /* nb_multiply */
1892 0, /* nb_remainder */
1893 0, /* nb_divmod */
1894 0, /* nb_power */
1895 0, /* nb_negative */
1896 0, /* nb_positive */
1897 0, /* nb_absolute */
1898 0, /* nb_bool */
1899 0, /* nb_invert */
1900 0, /* nb_lshift */
1901 0, /* nb_rshift */
1902 0, /* nb_and */
1903 0, /* nb_xor */
1904 0, /* nb_or */
1905 (unaryfunc)interpid_int, /* nb_int */
1906 0, /* nb_reserved */
1907 0, /* nb_float */
1908
1909 0, /* nb_inplace_add */
1910 0, /* nb_inplace_subtract */
1911 0, /* nb_inplace_multiply */
1912 0, /* nb_inplace_remainder */
1913 0, /* nb_inplace_power */
1914 0, /* nb_inplace_lshift */
1915 0, /* nb_inplace_rshift */
1916 0, /* nb_inplace_and */
1917 0, /* nb_inplace_xor */
1918 0, /* nb_inplace_or */
1919
1920 0, /* nb_floor_divide */
1921 0, /* nb_true_divide */
1922 0, /* nb_inplace_floor_divide */
1923 0, /* nb_inplace_true_divide */
1924
1925 (unaryfunc)interpid_int, /* nb_index */
1926};
1927
1928static Py_hash_t
1929interpid_hash(PyObject *self)
1930{
1931 interpid *id = (interpid *)self;
1932 PyObject *obj = PyLong_FromLongLong(id->id);
1933 if (obj == NULL) {
1934 return -1;
1935 }
1936 Py_hash_t hash = PyObject_Hash(obj);
1937 Py_DECREF(obj);
1938 return hash;
1939}
1940
1941static PyObject *
1942interpid_richcompare(PyObject *self, PyObject *other, int op)
1943{
1944 if (op != Py_EQ && op != Py_NE) {
1945 Py_RETURN_NOTIMPLEMENTED;
1946 }
1947
1948 if (!PyObject_TypeCheck(self, &InterpreterIDtype)) {
1949 Py_RETURN_NOTIMPLEMENTED;
1950 }
1951
1952 interpid *id = (interpid *)self;
1953 int equal;
1954 if (PyObject_TypeCheck(other, &InterpreterIDtype)) {
1955 interpid *otherid = (interpid *)other;
1956 equal = (id->id == otherid->id);
1957 }
1958 else {
1959 other = PyNumber_Long(other);
1960 if (other == NULL) {
1961 PyErr_Clear();
1962 Py_RETURN_NOTIMPLEMENTED;
1963 }
1964 int64_t otherid = PyLong_AsLongLong(other);
1965 Py_DECREF(other);
1966 if (otherid == -1 && PyErr_Occurred() != NULL) {
1967 return NULL;
1968 }
1969 if (otherid < 0) {
1970 equal = 0;
1971 }
1972 else {
1973 equal = (id->id == otherid);
1974 }
1975 }
1976
1977 if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) {
1978 Py_RETURN_TRUE;
1979 }
1980 Py_RETURN_FALSE;
1981}
1982
1983PyDoc_STRVAR(interpid_doc,
1984"A interpreter ID identifies a interpreter and may be used as an int.");
1985
1986static PyTypeObject InterpreterIDtype = {
1987 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1988 "interpreters.InterpreterID", /* tp_name */
1989 sizeof(interpid), /* tp_size */
1990 0, /* tp_itemsize */
1991 (destructor)interpid_dealloc, /* tp_dealloc */
1992 0, /* tp_print */
1993 0, /* tp_getattr */
1994 0, /* tp_setattr */
1995 0, /* tp_as_async */
1996 (reprfunc)interpid_repr, /* tp_repr */
1997 &interpid_as_number, /* tp_as_number */
1998 0, /* tp_as_sequence */
1999 0, /* tp_as_mapping */
2000 interpid_hash, /* tp_hash */
2001 0, /* tp_call */
2002 0, /* tp_str */
2003 0, /* tp_getattro */
2004 0, /* tp_setattro */
2005 0, /* tp_as_buffer */
2006 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
2007 Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */
2008 interpid_doc, /* tp_doc */
2009 0, /* tp_traverse */
2010 0, /* tp_clear */
2011 interpid_richcompare, /* tp_richcompare */
2012 0, /* tp_weaklistoffset */
2013 0, /* tp_iter */
2014 0, /* tp_iternext */
2015 0, /* tp_methods */
2016 0, /* tp_members */
2017 0, /* tp_getset */
2018 0, /* tp_base */
2019 0, /* tp_dict */
2020 0, /* tp_descr_get */
2021 0, /* tp_descr_set */
2022 0, /* tp_dictoffset */
2023 0, /* tp_init */
2024 0, /* tp_alloc */
2025 interpid_new, /* tp_new */
2026};
2027
2028static PyObject *
2029_get_id(PyInterpreterState *interp)
2030{
2031 PY_INT64_T id = PyInterpreterState_GetID(interp);
2032 if (id < 0) {
2033 return NULL;
2034 }
2035 return (PyObject *)newinterpid(&InterpreterIDtype, id, 0);
2036}
2037
2038static PyInterpreterState *
2039_look_up(PyObject *requested_id)
2040{
2041 int64_t id;
2042 if (PyObject_TypeCheck(requested_id, &InterpreterIDtype)) {
2043 id = ((interpid *)requested_id)->id;
2044 }
2045 else {
2046 id = PyLong_AsLongLong(requested_id);
2047 if (id == -1 && PyErr_Occurred() != NULL) {
2048 return NULL;
2049 }
2050 assert(id <= INT64_MAX);
2051 }
2052 return _PyInterpreterState_LookUpID(id);
2053}
2054
Eric Snow7f8bfc92018-01-29 18:23:44 -07002055
2056/* module level code ********************************************************/
2057
2058/* globals is the process-global state for the module. It holds all
2059 the data that we need to share between interpreters, so it cannot
2060 hold PyObject values. */
2061static struct globals {
2062 _channels channels;
2063} _globals = {{0}};
2064
2065static int
2066_init_globals(void)
2067{
2068 if (_channels_init(&_globals.channels) != 0) {
2069 return -1;
2070 }
2071 return 0;
2072}
2073
2074static _channels *
2075_global_channels(void) {
2076 return &_globals.channels;
2077}
2078
2079static PyObject *
2080interp_create(PyObject *self, PyObject *args)
2081{
2082 if (!PyArg_UnpackTuple(args, "create", 0, 0)) {
2083 return NULL;
2084 }
2085
2086 // Create and initialize the new interpreter.
Miss Islington (bot)eed3c7a2018-02-20 16:09:41 -08002087 PyThreadState *save_tstate = PyThreadState_Swap(NULL);
2088 // XXX Possible GILState issues?
2089 PyThreadState *tstate = Py_NewInterpreter();
Eric Snow7f8bfc92018-01-29 18:23:44 -07002090 PyThreadState_Swap(save_tstate);
2091 if (tstate == NULL) {
2092 /* Since no new thread state was created, there is no exception to
2093 propagate; raise a fresh one after swapping in the old thread
2094 state. */
2095 PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
2096 return NULL;
2097 }
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002098 if (_PyInterpreterState_IDInitref(tstate->interp) != 0) {
2099 goto error;
2100 };
Eric Snow7f8bfc92018-01-29 18:23:44 -07002101 return _get_id(tstate->interp);
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002102
2103error:
Miss Islington (bot)eed3c7a2018-02-20 16:09:41 -08002104 // XXX Possible GILState issues?
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002105 save_tstate = PyThreadState_Swap(tstate);
2106 Py_EndInterpreter(tstate);
2107 PyThreadState_Swap(save_tstate);
2108 return NULL;
Eric Snow7f8bfc92018-01-29 18:23:44 -07002109}
2110
2111PyDoc_STRVAR(create_doc,
2112"create() -> ID\n\
2113\n\
2114Create a new interpreter and return a unique generated ID.");
2115
2116
2117static PyObject *
2118interp_destroy(PyObject *self, PyObject *args)
2119{
2120 PyObject *id;
2121 if (!PyArg_UnpackTuple(args, "destroy", 1, 1, &id)) {
2122 return NULL;
2123 }
2124 if (!PyLong_Check(id)) {
2125 PyErr_SetString(PyExc_TypeError, "ID must be an int");
2126 return NULL;
2127 }
2128
2129 // Look up the interpreter.
2130 PyInterpreterState *interp = _look_up(id);
2131 if (interp == NULL) {
2132 return NULL;
2133 }
2134
2135 // Ensure we don't try to destroy the current interpreter.
2136 PyInterpreterState *current = _get_current();
2137 if (current == NULL) {
2138 return NULL;
2139 }
2140 if (interp == current) {
2141 PyErr_SetString(PyExc_RuntimeError,
2142 "cannot destroy the current interpreter");
2143 return NULL;
2144 }
2145
2146 // Ensure the interpreter isn't running.
2147 /* XXX We *could* support destroying a running interpreter but
2148 aren't going to worry about it for now. */
2149 if (_ensure_not_running(interp) < 0) {
2150 return NULL;
2151 }
2152
2153 // Destroy the interpreter.
2154 //PyInterpreterState_Delete(interp);
Miss Islington (bot)eed3c7a2018-02-20 16:09:41 -08002155 PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
2156 // XXX Possible GILState issues?
2157 PyThreadState *save_tstate = PyThreadState_Swap(tstate);
Eric Snow7f8bfc92018-01-29 18:23:44 -07002158 Py_EndInterpreter(tstate);
2159 PyThreadState_Swap(save_tstate);
2160
2161 Py_RETURN_NONE;
2162}
2163
2164PyDoc_STRVAR(destroy_doc,
2165"destroy(ID)\n\
2166\n\
2167Destroy the identified interpreter.\n\
2168\n\
2169Attempting to destroy the current interpreter results in a RuntimeError.\n\
2170So does an unrecognized ID.");
2171
2172
2173static PyObject *
2174interp_list_all(PyObject *self)
2175{
2176 PyObject *ids, *id;
2177 PyInterpreterState *interp;
2178
2179 ids = PyList_New(0);
2180 if (ids == NULL) {
2181 return NULL;
2182 }
2183
2184 interp = PyInterpreterState_Head();
2185 while (interp != NULL) {
2186 id = _get_id(interp);
2187 if (id == NULL) {
2188 Py_DECREF(ids);
2189 return NULL;
2190 }
2191 // insert at front of list
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002192 int res = PyList_Insert(ids, 0, id);
2193 Py_DECREF(id);
2194 if (res < 0) {
Eric Snow7f8bfc92018-01-29 18:23:44 -07002195 Py_DECREF(ids);
2196 return NULL;
2197 }
2198
2199 interp = PyInterpreterState_Next(interp);
2200 }
2201
2202 return ids;
2203}
2204
2205PyDoc_STRVAR(list_all_doc,
2206"list_all() -> [ID]\n\
2207\n\
2208Return a list containing the ID of every existing interpreter.");
2209
2210
2211static PyObject *
2212interp_get_current(PyObject *self)
2213{
2214 PyInterpreterState *interp =_get_current();
2215 if (interp == NULL) {
2216 return NULL;
2217 }
2218 return _get_id(interp);
2219}
2220
2221PyDoc_STRVAR(get_current_doc,
2222"get_current() -> ID\n\
2223\n\
2224Return the ID of current interpreter.");
2225
2226
2227static PyObject *
2228interp_get_main(PyObject *self)
2229{
2230 // Currently, 0 is always the main interpreter.
2231 return PyLong_FromLongLong(0);
2232}
2233
2234PyDoc_STRVAR(get_main_doc,
2235"get_main() -> ID\n\
2236\n\
2237Return the ID of main interpreter.");
2238
2239
2240static PyObject *
2241interp_run_string(PyObject *self, PyObject *args)
2242{
2243 PyObject *id, *code;
2244 PyObject *shared = NULL;
2245 if (!PyArg_UnpackTuple(args, "run_string", 2, 3, &id, &code, &shared)) {
2246 return NULL;
2247 }
2248 if (!PyLong_Check(id)) {
2249 PyErr_SetString(PyExc_TypeError, "first arg (ID) must be an int");
2250 return NULL;
2251 }
2252 if (!PyUnicode_Check(code)) {
2253 PyErr_SetString(PyExc_TypeError,
2254 "second arg (code) must be a string");
2255 return NULL;
2256 }
2257
2258 // Look up the interpreter.
2259 PyInterpreterState *interp = _look_up(id);
2260 if (interp == NULL) {
2261 return NULL;
2262 }
2263
2264 // Extract code.
2265 Py_ssize_t size;
2266 const char *codestr = PyUnicode_AsUTF8AndSize(code, &size);
2267 if (codestr == NULL) {
2268 return NULL;
2269 }
2270 if (strlen(codestr) != (size_t)size) {
2271 PyErr_SetString(PyExc_ValueError,
2272 "source code string cannot contain null bytes");
2273 return NULL;
2274 }
2275
2276 // Run the code in the interpreter.
2277 if (_run_script_in_interpreter(interp, codestr, shared) != 0) {
2278 return NULL;
2279 }
2280 Py_RETURN_NONE;
2281}
2282
2283PyDoc_STRVAR(run_string_doc,
2284"run_string(ID, sourcetext)\n\
2285\n\
2286Execute the provided string in the identified interpreter.\n\
2287\n\
2288See PyRun_SimpleStrings.");
2289
2290
2291static PyObject *
2292object_is_shareable(PyObject *self, PyObject *args)
2293{
2294 PyObject *obj;
2295 if (!PyArg_UnpackTuple(args, "is_shareable", 1, 1, &obj)) {
2296 return NULL;
2297 }
2298 if (_PyObject_CheckCrossInterpreterData(obj) == 0) {
2299 Py_RETURN_TRUE;
2300 }
2301 PyErr_Clear();
2302 Py_RETURN_FALSE;
2303}
2304
2305PyDoc_STRVAR(is_shareable_doc,
2306"is_shareable(obj) -> bool\n\
2307\n\
2308Return True if the object's data may be shared between interpreters and\n\
2309False otherwise.");
2310
2311
2312static PyObject *
2313interp_is_running(PyObject *self, PyObject *args)
2314{
2315 PyObject *id;
2316 if (!PyArg_UnpackTuple(args, "is_running", 1, 1, &id)) {
2317 return NULL;
2318 }
2319 if (!PyLong_Check(id)) {
2320 PyErr_SetString(PyExc_TypeError, "ID must be an int");
2321 return NULL;
2322 }
2323
2324 PyInterpreterState *interp = _look_up(id);
2325 if (interp == NULL) {
2326 return NULL;
2327 }
2328 int is_running = _is_running(interp);
2329 if (is_running < 0) {
2330 return NULL;
2331 }
2332 if (is_running) {
2333 Py_RETURN_TRUE;
2334 }
2335 Py_RETURN_FALSE;
2336}
2337
2338PyDoc_STRVAR(is_running_doc,
2339"is_running(id) -> bool\n\
2340\n\
2341Return whether or not the identified interpreter is running.");
2342
2343static PyObject *
2344channel_create(PyObject *self)
2345{
2346 int64_t cid = _channel_create(&_globals.channels);
2347 if (cid < 0) {
2348 return NULL;
2349 }
2350 PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0,
2351 &_globals.channels, 0);
2352 if (id == NULL) {
2353 if (_channel_destroy(&_globals.channels, cid) != 0) {
2354 // XXX issue a warning?
2355 }
2356 return NULL;
2357 }
2358 assert(((channelid *)id)->channels != NULL);
2359 return id;
2360}
2361
2362PyDoc_STRVAR(channel_create_doc,
2363"channel_create() -> ID\n\
2364\n\
2365Create a new cross-interpreter channel and return a unique generated ID.");
2366
2367static PyObject *
2368channel_destroy(PyObject *self, PyObject *args)
2369{
2370 PyObject *id;
2371 if (!PyArg_UnpackTuple(args, "channel_destroy", 1, 1, &id)) {
2372 return NULL;
2373 }
2374 int64_t cid = _coerce_id(id);
2375 if (cid < 0) {
2376 return NULL;
2377 }
2378
2379 if (_channel_destroy(&_globals.channels, cid) != 0) {
2380 return NULL;
2381 }
2382 Py_RETURN_NONE;
2383}
2384
2385PyDoc_STRVAR(channel_destroy_doc,
2386"channel_destroy(ID)\n\
2387\n\
2388Close and finalize the channel. Afterward attempts to use the channel\n\
2389will behave as though it never existed.");
2390
2391static PyObject *
2392channel_list_all(PyObject *self)
2393{
2394 int64_t count = 0;
2395 int64_t *cids = _channels_list_all(&_globals.channels, &count);
2396 if (cids == NULL) {
2397 if (count == 0) {
2398 return PyList_New(0);
2399 }
2400 return NULL;
2401 }
2402 PyObject *ids = PyList_New((Py_ssize_t)count);
2403 if (ids == NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002404 goto finally;
Eric Snow7f8bfc92018-01-29 18:23:44 -07002405 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002406 int64_t *cur = cids;
2407 for (int64_t i=0; i < count; cur++, i++) {
2408 PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0,
Eric Snow7f8bfc92018-01-29 18:23:44 -07002409 &_globals.channels, 0);
2410 if (id == NULL) {
2411 Py_DECREF(ids);
2412 ids = NULL;
2413 break;
2414 }
2415 PyList_SET_ITEM(ids, i, id);
2416 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002417
2418finally:
2419 PyMem_Free(cids);
Eric Snow7f8bfc92018-01-29 18:23:44 -07002420 return ids;
2421}
2422
2423PyDoc_STRVAR(channel_list_all_doc,
2424"channel_list_all() -> [ID]\n\
2425\n\
2426Return the list of all IDs for active channels.");
2427
2428static PyObject *
2429channel_send(PyObject *self, PyObject *args)
2430{
2431 PyObject *id;
2432 PyObject *obj;
2433 if (!PyArg_UnpackTuple(args, "channel_send", 2, 2, &id, &obj)) {
2434 return NULL;
2435 }
2436 int64_t cid = _coerce_id(id);
2437 if (cid < 0) {
2438 return NULL;
2439 }
2440
2441 if (_channel_send(&_globals.channels, cid, obj) != 0) {
2442 return NULL;
2443 }
2444 Py_RETURN_NONE;
2445}
2446
2447PyDoc_STRVAR(channel_send_doc,
2448"channel_send(ID, obj)\n\
2449\n\
2450Add the object's data to the channel's queue.");
2451
2452static PyObject *
2453channel_recv(PyObject *self, PyObject *args)
2454{
2455 PyObject *id;
2456 if (!PyArg_UnpackTuple(args, "channel_recv", 1, 1, &id)) {
2457 return NULL;
2458 }
2459 int64_t cid = _coerce_id(id);
2460 if (cid < 0) {
2461 return NULL;
2462 }
2463
2464 return _channel_recv(&_globals.channels, cid);
2465}
2466
2467PyDoc_STRVAR(channel_recv_doc,
2468"channel_recv(ID) -> obj\n\
2469\n\
2470Return a new object from the data at the from of the channel's queue.");
2471
2472static PyObject *
2473channel_close(PyObject *self, PyObject *args, PyObject *kwds)
2474{
2475 PyObject *id;
2476 if (!PyArg_UnpackTuple(args, "channel_recv", 1, 1, &id)) {
2477 return NULL;
2478 }
2479 int64_t cid = _coerce_id(id);
2480 if (cid < 0) {
2481 return NULL;
2482 }
2483
2484 if (_channel_close(&_globals.channels, cid) != 0) {
2485 return NULL;
2486 }
2487 Py_RETURN_NONE;
2488}
2489
2490PyDoc_STRVAR(channel_close_doc,
2491"channel_close(ID)\n\
2492\n\
2493Close the channel for all interpreters. Once the channel's ID has\n\
2494no more ref counts the channel will be destroyed.");
2495
2496static PyObject *
2497channel_drop_interpreter(PyObject *self, PyObject *args, PyObject *kwds)
2498{
2499 // Note that only the current interpreter is affected.
Eric Snow83e64c82018-01-29 21:04:15 -07002500 static char *kwlist[] = {"id", "send", "recv", NULL};
Eric Snow7f8bfc92018-01-29 18:23:44 -07002501 PyObject *id;
2502 int send = -1;
2503 int recv = -1;
2504 if (!PyArg_ParseTupleAndKeywords(args, kwds,
2505 "O|$pp:channel_drop_interpreter", kwlist,
2506 &id, &send, &recv))
2507 return NULL;
2508
2509 int64_t cid = _coerce_id(id);
2510 if (cid < 0) {
2511 return NULL;
2512 }
2513 if (send < 0 && recv < 0) {
2514 send = 1;
2515 recv = 1;
2516 }
2517 else {
2518 if (send < 0) {
2519 send = 0;
2520 }
2521 if (recv < 0) {
2522 recv = 0;
2523 }
2524 }
2525 if (_channel_drop(&_globals.channels, cid, send, recv) != 0) {
2526 return NULL;
2527 }
2528 Py_RETURN_NONE;
2529}
2530
2531PyDoc_STRVAR(channel_drop_interpreter_doc,
2532"channel_drop_interpreter(ID, *, send=None, recv=None)\n\
2533\n\
2534Close the channel for the current interpreter. 'send' and 'recv'\n\
2535(bool) may be used to indicate the ends to close. By default both\n\
2536ends are closed. Closing an already closed end is a noop.");
2537
2538static PyObject *
2539channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
2540{
2541 return channelid_new(&ChannelIDtype, args, kwds);
2542}
2543
2544static PyMethodDef module_functions[] = {
2545 {"create", (PyCFunction)interp_create,
2546 METH_VARARGS, create_doc},
2547 {"destroy", (PyCFunction)interp_destroy,
2548 METH_VARARGS, destroy_doc},
2549 {"list_all", (PyCFunction)interp_list_all,
2550 METH_NOARGS, list_all_doc},
2551 {"get_current", (PyCFunction)interp_get_current,
2552 METH_NOARGS, get_current_doc},
2553 {"get_main", (PyCFunction)interp_get_main,
2554 METH_NOARGS, get_main_doc},
2555 {"is_running", (PyCFunction)interp_is_running,
2556 METH_VARARGS, is_running_doc},
2557 {"run_string", (PyCFunction)interp_run_string,
2558 METH_VARARGS, run_string_doc},
2559
2560 {"is_shareable", (PyCFunction)object_is_shareable,
2561 METH_VARARGS, is_shareable_doc},
2562
2563 {"channel_create", (PyCFunction)channel_create,
2564 METH_NOARGS, channel_create_doc},
2565 {"channel_destroy", (PyCFunction)channel_destroy,
2566 METH_VARARGS, channel_destroy_doc},
2567 {"channel_list_all", (PyCFunction)channel_list_all,
2568 METH_NOARGS, channel_list_all_doc},
2569 {"channel_send", (PyCFunction)channel_send,
2570 METH_VARARGS, channel_send_doc},
2571 {"channel_recv", (PyCFunction)channel_recv,
2572 METH_VARARGS, channel_recv_doc},
2573 {"channel_close", (PyCFunction)channel_close,
2574 METH_VARARGS, channel_close_doc},
2575 {"channel_drop_interpreter", (PyCFunction)channel_drop_interpreter,
2576 METH_VARARGS | METH_KEYWORDS, channel_drop_interpreter_doc},
2577 {"_channel_id", (PyCFunction)channel__channel_id,
2578 METH_VARARGS | METH_KEYWORDS, NULL},
2579
2580 {NULL, NULL} /* sentinel */
2581};
2582
2583
2584/* initialization function */
2585
2586PyDoc_STRVAR(module_doc,
2587"This module provides primitive operations to manage Python interpreters.\n\
2588The 'interpreters' module provides a more convenient interface.");
2589
2590static struct PyModuleDef interpretersmodule = {
2591 PyModuleDef_HEAD_INIT,
2592 "_xxsubinterpreters", /* m_name */
2593 module_doc, /* m_doc */
2594 -1, /* m_size */
2595 module_functions, /* m_methods */
2596 NULL, /* m_slots */
2597 NULL, /* m_traverse */
2598 NULL, /* m_clear */
2599 NULL /* m_free */
2600};
2601
2602
2603PyMODINIT_FUNC
2604PyInit__xxsubinterpreters(void)
2605{
2606 if (_init_globals() != 0) {
2607 return NULL;
2608 }
2609
2610 /* Initialize types */
2611 ChannelIDtype.tp_base = &PyLong_Type;
2612 if (PyType_Ready(&ChannelIDtype) != 0) {
2613 return NULL;
2614 }
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002615 InterpreterIDtype.tp_base = &PyLong_Type;
2616 if (PyType_Ready(&InterpreterIDtype) != 0) {
2617 return NULL;
2618 }
Eric Snow7f8bfc92018-01-29 18:23:44 -07002619
2620 /* Create the module */
2621 PyObject *module = PyModule_Create(&interpretersmodule);
2622 if (module == NULL) {
2623 return NULL;
2624 }
2625
2626 /* Add exception types */
2627 PyObject *ns = PyModule_GetDict(module); // borrowed
2628 if (interp_exceptions_init(ns) != 0) {
2629 return NULL;
2630 }
2631 if (channel_exceptions_init(ns) != 0) {
2632 return NULL;
2633 }
2634
2635 /* Add other types */
2636 Py_INCREF(&ChannelIDtype);
2637 if (PyDict_SetItemString(ns, "ChannelID", (PyObject *)&ChannelIDtype) != 0) {
2638 return NULL;
2639 }
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002640 Py_INCREF(&InterpreterIDtype);
2641 if (PyDict_SetItemString(ns, "InterpreterID", (PyObject *)&InterpreterIDtype) != 0) {
2642 return NULL;
2643 }
Eric Snow7f8bfc92018-01-29 18:23:44 -07002644
2645 if (_PyCrossInterpreterData_Register_Class(&ChannelIDtype, _channelid_shared)) {
2646 return NULL;
2647 }
2648
2649 return module;
2650}