blob: d7588079f225c151746a32b7d2caa69131d94abe [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.
1762 PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1763 PyThreadState *save_tstate = PyThreadState_Swap(tstate);
1764
1765 // Run the script.
1766 _sharedexception *exc = NULL;
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001767 int result = _run_script(interp, codestr, shared, &exc);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001768
1769 // Switch back.
1770 if (save_tstate != NULL) {
1771 PyThreadState_Swap(save_tstate);
1772 }
1773
1774 // Propagate any exception out to the caller.
1775 if (exc != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001776 _sharedexception_apply(exc, RunFailedError);
1777 _sharedexception_free(exc);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001778 }
1779 else if (result != 0) {
1780 // We were unable to allocate a shared exception.
1781 PyErr_NoMemory();
1782 }
1783
1784 if (shared != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001785 _sharedns_free(shared);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001786 }
1787
1788 return result;
1789}
1790
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08001791/* InterpreterID class */
1792
1793static PyTypeObject InterpreterIDtype;
1794
1795typedef struct interpid {
1796 PyObject_HEAD
1797 int64_t id;
1798} interpid;
1799
1800static interpid *
1801newinterpid(PyTypeObject *cls, int64_t id, int force)
1802{
1803 PyInterpreterState *interp = _PyInterpreterState_LookUpID(id);
1804 if (interp == NULL) {
1805 if (force) {
1806 PyErr_Clear();
1807 }
1808 else {
1809 return NULL;
1810 }
1811 }
1812
1813 interpid *self = PyObject_New(interpid, cls);
1814 if (self == NULL) {
1815 return NULL;
1816 }
1817 self->id = id;
1818
1819 if (interp != NULL) {
1820 _PyInterpreterState_IDIncref(interp);
1821 }
1822 return self;
1823}
1824
1825static PyObject *
1826interpid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds)
1827{
1828 static char *kwlist[] = {"id", "force", NULL};
1829 PyObject *idobj;
1830 int force = 0;
1831 if (!PyArg_ParseTupleAndKeywords(args, kwds,
1832 "O|$p:InterpreterID.__init__", kwlist,
1833 &idobj, &force)) {
1834 return NULL;
1835 }
1836
1837 // Coerce and check the ID.
1838 int64_t id;
1839 if (PyObject_TypeCheck(idobj, &InterpreterIDtype)) {
1840 id = ((interpid *)idobj)->id;
1841 }
1842 else {
1843 id = _coerce_id(idobj);
1844 if (id < 0) {
1845 return NULL;
1846 }
1847 }
1848
1849 return (PyObject *)newinterpid(cls, id, force);
1850}
1851
1852static void
1853interpid_dealloc(PyObject *v)
1854{
1855 int64_t id = ((interpid *)v)->id;
1856 PyInterpreterState *interp = _PyInterpreterState_LookUpID(id);
1857 if (interp != NULL) {
1858 _PyInterpreterState_IDDecref(interp);
1859 }
1860 else {
1861 // already deleted
1862 PyErr_Clear();
1863 }
1864 Py_TYPE(v)->tp_free(v);
1865}
1866
1867static PyObject *
1868interpid_repr(PyObject *self)
1869{
1870 PyTypeObject *type = Py_TYPE(self);
1871 const char *name = _PyType_Name(type);
1872 interpid *id = (interpid *)self;
1873 return PyUnicode_FromFormat("%s(%d)", name, id->id);
1874}
1875
1876PyObject *
1877interpid_int(PyObject *self)
1878{
1879 interpid *id = (interpid *)self;
1880 return PyLong_FromLongLong(id->id);
1881}
1882
1883static PyNumberMethods interpid_as_number = {
1884 0, /* nb_add */
1885 0, /* nb_subtract */
1886 0, /* nb_multiply */
1887 0, /* nb_remainder */
1888 0, /* nb_divmod */
1889 0, /* nb_power */
1890 0, /* nb_negative */
1891 0, /* nb_positive */
1892 0, /* nb_absolute */
1893 0, /* nb_bool */
1894 0, /* nb_invert */
1895 0, /* nb_lshift */
1896 0, /* nb_rshift */
1897 0, /* nb_and */
1898 0, /* nb_xor */
1899 0, /* nb_or */
1900 (unaryfunc)interpid_int, /* nb_int */
1901 0, /* nb_reserved */
1902 0, /* nb_float */
1903
1904 0, /* nb_inplace_add */
1905 0, /* nb_inplace_subtract */
1906 0, /* nb_inplace_multiply */
1907 0, /* nb_inplace_remainder */
1908 0, /* nb_inplace_power */
1909 0, /* nb_inplace_lshift */
1910 0, /* nb_inplace_rshift */
1911 0, /* nb_inplace_and */
1912 0, /* nb_inplace_xor */
1913 0, /* nb_inplace_or */
1914
1915 0, /* nb_floor_divide */
1916 0, /* nb_true_divide */
1917 0, /* nb_inplace_floor_divide */
1918 0, /* nb_inplace_true_divide */
1919
1920 (unaryfunc)interpid_int, /* nb_index */
1921};
1922
1923static Py_hash_t
1924interpid_hash(PyObject *self)
1925{
1926 interpid *id = (interpid *)self;
1927 PyObject *obj = PyLong_FromLongLong(id->id);
1928 if (obj == NULL) {
1929 return -1;
1930 }
1931 Py_hash_t hash = PyObject_Hash(obj);
1932 Py_DECREF(obj);
1933 return hash;
1934}
1935
1936static PyObject *
1937interpid_richcompare(PyObject *self, PyObject *other, int op)
1938{
1939 if (op != Py_EQ && op != Py_NE) {
1940 Py_RETURN_NOTIMPLEMENTED;
1941 }
1942
1943 if (!PyObject_TypeCheck(self, &InterpreterIDtype)) {
1944 Py_RETURN_NOTIMPLEMENTED;
1945 }
1946
1947 interpid *id = (interpid *)self;
1948 int equal;
1949 if (PyObject_TypeCheck(other, &InterpreterIDtype)) {
1950 interpid *otherid = (interpid *)other;
1951 equal = (id->id == otherid->id);
1952 }
1953 else {
1954 other = PyNumber_Long(other);
1955 if (other == NULL) {
1956 PyErr_Clear();
1957 Py_RETURN_NOTIMPLEMENTED;
1958 }
1959 int64_t otherid = PyLong_AsLongLong(other);
1960 Py_DECREF(other);
1961 if (otherid == -1 && PyErr_Occurred() != NULL) {
1962 return NULL;
1963 }
1964 if (otherid < 0) {
1965 equal = 0;
1966 }
1967 else {
1968 equal = (id->id == otherid);
1969 }
1970 }
1971
1972 if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) {
1973 Py_RETURN_TRUE;
1974 }
1975 Py_RETURN_FALSE;
1976}
1977
1978PyDoc_STRVAR(interpid_doc,
1979"A interpreter ID identifies a interpreter and may be used as an int.");
1980
1981static PyTypeObject InterpreterIDtype = {
1982 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1983 "interpreters.InterpreterID", /* tp_name */
1984 sizeof(interpid), /* tp_size */
1985 0, /* tp_itemsize */
1986 (destructor)interpid_dealloc, /* tp_dealloc */
1987 0, /* tp_print */
1988 0, /* tp_getattr */
1989 0, /* tp_setattr */
1990 0, /* tp_as_async */
1991 (reprfunc)interpid_repr, /* tp_repr */
1992 &interpid_as_number, /* tp_as_number */
1993 0, /* tp_as_sequence */
1994 0, /* tp_as_mapping */
1995 interpid_hash, /* tp_hash */
1996 0, /* tp_call */
1997 0, /* tp_str */
1998 0, /* tp_getattro */
1999 0, /* tp_setattro */
2000 0, /* tp_as_buffer */
2001 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
2002 Py_TPFLAGS_LONG_SUBCLASS, /* tp_flags */
2003 interpid_doc, /* tp_doc */
2004 0, /* tp_traverse */
2005 0, /* tp_clear */
2006 interpid_richcompare, /* tp_richcompare */
2007 0, /* tp_weaklistoffset */
2008 0, /* tp_iter */
2009 0, /* tp_iternext */
2010 0, /* tp_methods */
2011 0, /* tp_members */
2012 0, /* tp_getset */
2013 0, /* tp_base */
2014 0, /* tp_dict */
2015 0, /* tp_descr_get */
2016 0, /* tp_descr_set */
2017 0, /* tp_dictoffset */
2018 0, /* tp_init */
2019 0, /* tp_alloc */
2020 interpid_new, /* tp_new */
2021};
2022
2023static PyObject *
2024_get_id(PyInterpreterState *interp)
2025{
2026 PY_INT64_T id = PyInterpreterState_GetID(interp);
2027 if (id < 0) {
2028 return NULL;
2029 }
2030 return (PyObject *)newinterpid(&InterpreterIDtype, id, 0);
2031}
2032
2033static PyInterpreterState *
2034_look_up(PyObject *requested_id)
2035{
2036 int64_t id;
2037 if (PyObject_TypeCheck(requested_id, &InterpreterIDtype)) {
2038 id = ((interpid *)requested_id)->id;
2039 }
2040 else {
2041 id = PyLong_AsLongLong(requested_id);
2042 if (id == -1 && PyErr_Occurred() != NULL) {
2043 return NULL;
2044 }
2045 assert(id <= INT64_MAX);
2046 }
2047 return _PyInterpreterState_LookUpID(id);
2048}
2049
Eric Snow7f8bfc92018-01-29 18:23:44 -07002050
2051/* module level code ********************************************************/
2052
2053/* globals is the process-global state for the module. It holds all
2054 the data that we need to share between interpreters, so it cannot
2055 hold PyObject values. */
2056static struct globals {
2057 _channels channels;
2058} _globals = {{0}};
2059
2060static int
2061_init_globals(void)
2062{
2063 if (_channels_init(&_globals.channels) != 0) {
2064 return -1;
2065 }
2066 return 0;
2067}
2068
2069static _channels *
2070_global_channels(void) {
2071 return &_globals.channels;
2072}
2073
2074static PyObject *
2075interp_create(PyObject *self, PyObject *args)
2076{
2077 if (!PyArg_UnpackTuple(args, "create", 0, 0)) {
2078 return NULL;
2079 }
2080
2081 // Create and initialize the new interpreter.
2082 PyThreadState *tstate, *save_tstate;
2083 save_tstate = PyThreadState_Swap(NULL);
2084 tstate = Py_NewInterpreter();
2085 PyThreadState_Swap(save_tstate);
2086 if (tstate == NULL) {
2087 /* Since no new thread state was created, there is no exception to
2088 propagate; raise a fresh one after swapping in the old thread
2089 state. */
2090 PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
2091 return NULL;
2092 }
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002093 if (_PyInterpreterState_IDInitref(tstate->interp) != 0) {
2094 goto error;
2095 };
Eric Snow7f8bfc92018-01-29 18:23:44 -07002096 return _get_id(tstate->interp);
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002097
2098error:
2099 save_tstate = PyThreadState_Swap(tstate);
2100 Py_EndInterpreter(tstate);
2101 PyThreadState_Swap(save_tstate);
2102 return NULL;
Eric Snow7f8bfc92018-01-29 18:23:44 -07002103}
2104
2105PyDoc_STRVAR(create_doc,
2106"create() -> ID\n\
2107\n\
2108Create a new interpreter and return a unique generated ID.");
2109
2110
2111static PyObject *
2112interp_destroy(PyObject *self, PyObject *args)
2113{
2114 PyObject *id;
2115 if (!PyArg_UnpackTuple(args, "destroy", 1, 1, &id)) {
2116 return NULL;
2117 }
2118 if (!PyLong_Check(id)) {
2119 PyErr_SetString(PyExc_TypeError, "ID must be an int");
2120 return NULL;
2121 }
2122
2123 // Look up the interpreter.
2124 PyInterpreterState *interp = _look_up(id);
2125 if (interp == NULL) {
2126 return NULL;
2127 }
2128
2129 // Ensure we don't try to destroy the current interpreter.
2130 PyInterpreterState *current = _get_current();
2131 if (current == NULL) {
2132 return NULL;
2133 }
2134 if (interp == current) {
2135 PyErr_SetString(PyExc_RuntimeError,
2136 "cannot destroy the current interpreter");
2137 return NULL;
2138 }
2139
2140 // Ensure the interpreter isn't running.
2141 /* XXX We *could* support destroying a running interpreter but
2142 aren't going to worry about it for now. */
2143 if (_ensure_not_running(interp) < 0) {
2144 return NULL;
2145 }
2146
2147 // Destroy the interpreter.
2148 //PyInterpreterState_Delete(interp);
2149 PyThreadState *tstate, *save_tstate;
2150 tstate = PyInterpreterState_ThreadHead(interp);
2151 save_tstate = PyThreadState_Swap(tstate);
2152 Py_EndInterpreter(tstate);
2153 PyThreadState_Swap(save_tstate);
2154
2155 Py_RETURN_NONE;
2156}
2157
2158PyDoc_STRVAR(destroy_doc,
2159"destroy(ID)\n\
2160\n\
2161Destroy the identified interpreter.\n\
2162\n\
2163Attempting to destroy the current interpreter results in a RuntimeError.\n\
2164So does an unrecognized ID.");
2165
2166
2167static PyObject *
2168interp_list_all(PyObject *self)
2169{
2170 PyObject *ids, *id;
2171 PyInterpreterState *interp;
2172
2173 ids = PyList_New(0);
2174 if (ids == NULL) {
2175 return NULL;
2176 }
2177
2178 interp = PyInterpreterState_Head();
2179 while (interp != NULL) {
2180 id = _get_id(interp);
2181 if (id == NULL) {
2182 Py_DECREF(ids);
2183 return NULL;
2184 }
2185 // insert at front of list
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002186 int res = PyList_Insert(ids, 0, id);
2187 Py_DECREF(id);
2188 if (res < 0) {
Eric Snow7f8bfc92018-01-29 18:23:44 -07002189 Py_DECREF(ids);
2190 return NULL;
2191 }
2192
2193 interp = PyInterpreterState_Next(interp);
2194 }
2195
2196 return ids;
2197}
2198
2199PyDoc_STRVAR(list_all_doc,
2200"list_all() -> [ID]\n\
2201\n\
2202Return a list containing the ID of every existing interpreter.");
2203
2204
2205static PyObject *
2206interp_get_current(PyObject *self)
2207{
2208 PyInterpreterState *interp =_get_current();
2209 if (interp == NULL) {
2210 return NULL;
2211 }
2212 return _get_id(interp);
2213}
2214
2215PyDoc_STRVAR(get_current_doc,
2216"get_current() -> ID\n\
2217\n\
2218Return the ID of current interpreter.");
2219
2220
2221static PyObject *
2222interp_get_main(PyObject *self)
2223{
2224 // Currently, 0 is always the main interpreter.
2225 return PyLong_FromLongLong(0);
2226}
2227
2228PyDoc_STRVAR(get_main_doc,
2229"get_main() -> ID\n\
2230\n\
2231Return the ID of main interpreter.");
2232
2233
2234static PyObject *
2235interp_run_string(PyObject *self, PyObject *args)
2236{
2237 PyObject *id, *code;
2238 PyObject *shared = NULL;
2239 if (!PyArg_UnpackTuple(args, "run_string", 2, 3, &id, &code, &shared)) {
2240 return NULL;
2241 }
2242 if (!PyLong_Check(id)) {
2243 PyErr_SetString(PyExc_TypeError, "first arg (ID) must be an int");
2244 return NULL;
2245 }
2246 if (!PyUnicode_Check(code)) {
2247 PyErr_SetString(PyExc_TypeError,
2248 "second arg (code) must be a string");
2249 return NULL;
2250 }
2251
2252 // Look up the interpreter.
2253 PyInterpreterState *interp = _look_up(id);
2254 if (interp == NULL) {
2255 return NULL;
2256 }
2257
2258 // Extract code.
2259 Py_ssize_t size;
2260 const char *codestr = PyUnicode_AsUTF8AndSize(code, &size);
2261 if (codestr == NULL) {
2262 return NULL;
2263 }
2264 if (strlen(codestr) != (size_t)size) {
2265 PyErr_SetString(PyExc_ValueError,
2266 "source code string cannot contain null bytes");
2267 return NULL;
2268 }
2269
2270 // Run the code in the interpreter.
2271 if (_run_script_in_interpreter(interp, codestr, shared) != 0) {
2272 return NULL;
2273 }
2274 Py_RETURN_NONE;
2275}
2276
2277PyDoc_STRVAR(run_string_doc,
2278"run_string(ID, sourcetext)\n\
2279\n\
2280Execute the provided string in the identified interpreter.\n\
2281\n\
2282See PyRun_SimpleStrings.");
2283
2284
2285static PyObject *
2286object_is_shareable(PyObject *self, PyObject *args)
2287{
2288 PyObject *obj;
2289 if (!PyArg_UnpackTuple(args, "is_shareable", 1, 1, &obj)) {
2290 return NULL;
2291 }
2292 if (_PyObject_CheckCrossInterpreterData(obj) == 0) {
2293 Py_RETURN_TRUE;
2294 }
2295 PyErr_Clear();
2296 Py_RETURN_FALSE;
2297}
2298
2299PyDoc_STRVAR(is_shareable_doc,
2300"is_shareable(obj) -> bool\n\
2301\n\
2302Return True if the object's data may be shared between interpreters and\n\
2303False otherwise.");
2304
2305
2306static PyObject *
2307interp_is_running(PyObject *self, PyObject *args)
2308{
2309 PyObject *id;
2310 if (!PyArg_UnpackTuple(args, "is_running", 1, 1, &id)) {
2311 return NULL;
2312 }
2313 if (!PyLong_Check(id)) {
2314 PyErr_SetString(PyExc_TypeError, "ID must be an int");
2315 return NULL;
2316 }
2317
2318 PyInterpreterState *interp = _look_up(id);
2319 if (interp == NULL) {
2320 return NULL;
2321 }
2322 int is_running = _is_running(interp);
2323 if (is_running < 0) {
2324 return NULL;
2325 }
2326 if (is_running) {
2327 Py_RETURN_TRUE;
2328 }
2329 Py_RETURN_FALSE;
2330}
2331
2332PyDoc_STRVAR(is_running_doc,
2333"is_running(id) -> bool\n\
2334\n\
2335Return whether or not the identified interpreter is running.");
2336
2337static PyObject *
2338channel_create(PyObject *self)
2339{
2340 int64_t cid = _channel_create(&_globals.channels);
2341 if (cid < 0) {
2342 return NULL;
2343 }
2344 PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0,
2345 &_globals.channels, 0);
2346 if (id == NULL) {
2347 if (_channel_destroy(&_globals.channels, cid) != 0) {
2348 // XXX issue a warning?
2349 }
2350 return NULL;
2351 }
2352 assert(((channelid *)id)->channels != NULL);
2353 return id;
2354}
2355
2356PyDoc_STRVAR(channel_create_doc,
2357"channel_create() -> ID\n\
2358\n\
2359Create a new cross-interpreter channel and return a unique generated ID.");
2360
2361static PyObject *
2362channel_destroy(PyObject *self, PyObject *args)
2363{
2364 PyObject *id;
2365 if (!PyArg_UnpackTuple(args, "channel_destroy", 1, 1, &id)) {
2366 return NULL;
2367 }
2368 int64_t cid = _coerce_id(id);
2369 if (cid < 0) {
2370 return NULL;
2371 }
2372
2373 if (_channel_destroy(&_globals.channels, cid) != 0) {
2374 return NULL;
2375 }
2376 Py_RETURN_NONE;
2377}
2378
2379PyDoc_STRVAR(channel_destroy_doc,
2380"channel_destroy(ID)\n\
2381\n\
2382Close and finalize the channel. Afterward attempts to use the channel\n\
2383will behave as though it never existed.");
2384
2385static PyObject *
2386channel_list_all(PyObject *self)
2387{
2388 int64_t count = 0;
2389 int64_t *cids = _channels_list_all(&_globals.channels, &count);
2390 if (cids == NULL) {
2391 if (count == 0) {
2392 return PyList_New(0);
2393 }
2394 return NULL;
2395 }
2396 PyObject *ids = PyList_New((Py_ssize_t)count);
2397 if (ids == NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002398 goto finally;
Eric Snow7f8bfc92018-01-29 18:23:44 -07002399 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002400 int64_t *cur = cids;
2401 for (int64_t i=0; i < count; cur++, i++) {
2402 PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0,
Eric Snow7f8bfc92018-01-29 18:23:44 -07002403 &_globals.channels, 0);
2404 if (id == NULL) {
2405 Py_DECREF(ids);
2406 ids = NULL;
2407 break;
2408 }
2409 PyList_SET_ITEM(ids, i, id);
2410 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002411
2412finally:
2413 PyMem_Free(cids);
Eric Snow7f8bfc92018-01-29 18:23:44 -07002414 return ids;
2415}
2416
2417PyDoc_STRVAR(channel_list_all_doc,
2418"channel_list_all() -> [ID]\n\
2419\n\
2420Return the list of all IDs for active channels.");
2421
2422static PyObject *
2423channel_send(PyObject *self, PyObject *args)
2424{
2425 PyObject *id;
2426 PyObject *obj;
2427 if (!PyArg_UnpackTuple(args, "channel_send", 2, 2, &id, &obj)) {
2428 return NULL;
2429 }
2430 int64_t cid = _coerce_id(id);
2431 if (cid < 0) {
2432 return NULL;
2433 }
2434
2435 if (_channel_send(&_globals.channels, cid, obj) != 0) {
2436 return NULL;
2437 }
2438 Py_RETURN_NONE;
2439}
2440
2441PyDoc_STRVAR(channel_send_doc,
2442"channel_send(ID, obj)\n\
2443\n\
2444Add the object's data to the channel's queue.");
2445
2446static PyObject *
2447channel_recv(PyObject *self, PyObject *args)
2448{
2449 PyObject *id;
2450 if (!PyArg_UnpackTuple(args, "channel_recv", 1, 1, &id)) {
2451 return NULL;
2452 }
2453 int64_t cid = _coerce_id(id);
2454 if (cid < 0) {
2455 return NULL;
2456 }
2457
2458 return _channel_recv(&_globals.channels, cid);
2459}
2460
2461PyDoc_STRVAR(channel_recv_doc,
2462"channel_recv(ID) -> obj\n\
2463\n\
2464Return a new object from the data at the from of the channel's queue.");
2465
2466static PyObject *
2467channel_close(PyObject *self, PyObject *args, PyObject *kwds)
2468{
2469 PyObject *id;
2470 if (!PyArg_UnpackTuple(args, "channel_recv", 1, 1, &id)) {
2471 return NULL;
2472 }
2473 int64_t cid = _coerce_id(id);
2474 if (cid < 0) {
2475 return NULL;
2476 }
2477
2478 if (_channel_close(&_globals.channels, cid) != 0) {
2479 return NULL;
2480 }
2481 Py_RETURN_NONE;
2482}
2483
2484PyDoc_STRVAR(channel_close_doc,
2485"channel_close(ID)\n\
2486\n\
2487Close the channel for all interpreters. Once the channel's ID has\n\
2488no more ref counts the channel will be destroyed.");
2489
2490static PyObject *
2491channel_drop_interpreter(PyObject *self, PyObject *args, PyObject *kwds)
2492{
2493 // Note that only the current interpreter is affected.
Eric Snow83e64c82018-01-29 21:04:15 -07002494 static char *kwlist[] = {"id", "send", "recv", NULL};
Eric Snow7f8bfc92018-01-29 18:23:44 -07002495 PyObject *id;
2496 int send = -1;
2497 int recv = -1;
2498 if (!PyArg_ParseTupleAndKeywords(args, kwds,
2499 "O|$pp:channel_drop_interpreter", kwlist,
2500 &id, &send, &recv))
2501 return NULL;
2502
2503 int64_t cid = _coerce_id(id);
2504 if (cid < 0) {
2505 return NULL;
2506 }
2507 if (send < 0 && recv < 0) {
2508 send = 1;
2509 recv = 1;
2510 }
2511 else {
2512 if (send < 0) {
2513 send = 0;
2514 }
2515 if (recv < 0) {
2516 recv = 0;
2517 }
2518 }
2519 if (_channel_drop(&_globals.channels, cid, send, recv) != 0) {
2520 return NULL;
2521 }
2522 Py_RETURN_NONE;
2523}
2524
2525PyDoc_STRVAR(channel_drop_interpreter_doc,
2526"channel_drop_interpreter(ID, *, send=None, recv=None)\n\
2527\n\
2528Close the channel for the current interpreter. 'send' and 'recv'\n\
2529(bool) may be used to indicate the ends to close. By default both\n\
2530ends are closed. Closing an already closed end is a noop.");
2531
2532static PyObject *
2533channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
2534{
2535 return channelid_new(&ChannelIDtype, args, kwds);
2536}
2537
2538static PyMethodDef module_functions[] = {
2539 {"create", (PyCFunction)interp_create,
2540 METH_VARARGS, create_doc},
2541 {"destroy", (PyCFunction)interp_destroy,
2542 METH_VARARGS, destroy_doc},
2543 {"list_all", (PyCFunction)interp_list_all,
2544 METH_NOARGS, list_all_doc},
2545 {"get_current", (PyCFunction)interp_get_current,
2546 METH_NOARGS, get_current_doc},
2547 {"get_main", (PyCFunction)interp_get_main,
2548 METH_NOARGS, get_main_doc},
2549 {"is_running", (PyCFunction)interp_is_running,
2550 METH_VARARGS, is_running_doc},
2551 {"run_string", (PyCFunction)interp_run_string,
2552 METH_VARARGS, run_string_doc},
2553
2554 {"is_shareable", (PyCFunction)object_is_shareable,
2555 METH_VARARGS, is_shareable_doc},
2556
2557 {"channel_create", (PyCFunction)channel_create,
2558 METH_NOARGS, channel_create_doc},
2559 {"channel_destroy", (PyCFunction)channel_destroy,
2560 METH_VARARGS, channel_destroy_doc},
2561 {"channel_list_all", (PyCFunction)channel_list_all,
2562 METH_NOARGS, channel_list_all_doc},
2563 {"channel_send", (PyCFunction)channel_send,
2564 METH_VARARGS, channel_send_doc},
2565 {"channel_recv", (PyCFunction)channel_recv,
2566 METH_VARARGS, channel_recv_doc},
2567 {"channel_close", (PyCFunction)channel_close,
2568 METH_VARARGS, channel_close_doc},
2569 {"channel_drop_interpreter", (PyCFunction)channel_drop_interpreter,
2570 METH_VARARGS | METH_KEYWORDS, channel_drop_interpreter_doc},
2571 {"_channel_id", (PyCFunction)channel__channel_id,
2572 METH_VARARGS | METH_KEYWORDS, NULL},
2573
2574 {NULL, NULL} /* sentinel */
2575};
2576
2577
2578/* initialization function */
2579
2580PyDoc_STRVAR(module_doc,
2581"This module provides primitive operations to manage Python interpreters.\n\
2582The 'interpreters' module provides a more convenient interface.");
2583
2584static struct PyModuleDef interpretersmodule = {
2585 PyModuleDef_HEAD_INIT,
2586 "_xxsubinterpreters", /* m_name */
2587 module_doc, /* m_doc */
2588 -1, /* m_size */
2589 module_functions, /* m_methods */
2590 NULL, /* m_slots */
2591 NULL, /* m_traverse */
2592 NULL, /* m_clear */
2593 NULL /* m_free */
2594};
2595
2596
2597PyMODINIT_FUNC
2598PyInit__xxsubinterpreters(void)
2599{
2600 if (_init_globals() != 0) {
2601 return NULL;
2602 }
2603
2604 /* Initialize types */
2605 ChannelIDtype.tp_base = &PyLong_Type;
2606 if (PyType_Ready(&ChannelIDtype) != 0) {
2607 return NULL;
2608 }
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002609 InterpreterIDtype.tp_base = &PyLong_Type;
2610 if (PyType_Ready(&InterpreterIDtype) != 0) {
2611 return NULL;
2612 }
Eric Snow7f8bfc92018-01-29 18:23:44 -07002613
2614 /* Create the module */
2615 PyObject *module = PyModule_Create(&interpretersmodule);
2616 if (module == NULL) {
2617 return NULL;
2618 }
2619
2620 /* Add exception types */
2621 PyObject *ns = PyModule_GetDict(module); // borrowed
2622 if (interp_exceptions_init(ns) != 0) {
2623 return NULL;
2624 }
2625 if (channel_exceptions_init(ns) != 0) {
2626 return NULL;
2627 }
2628
2629 /* Add other types */
2630 Py_INCREF(&ChannelIDtype);
2631 if (PyDict_SetItemString(ns, "ChannelID", (PyObject *)&ChannelIDtype) != 0) {
2632 return NULL;
2633 }
Miss Islington (bot)3db05a32018-02-16 18:15:24 -08002634 Py_INCREF(&InterpreterIDtype);
2635 if (PyDict_SetItemString(ns, "InterpreterID", (PyObject *)&InterpreterIDtype) != 0) {
2636 return NULL;
2637 }
Eric Snow7f8bfc92018-01-29 18:23:44 -07002638
2639 if (_PyCrossInterpreterData_Register_Class(&ChannelIDtype, _channelid_shared)) {
2640 return NULL;
2641 }
2642
2643 return module;
2644}