blob: 7829b4cd951156648f1cb16f1e43a3ee288b9270 [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
1656static PyInterpreterState *
1657_look_up(PyObject *requested_id)
1658{
1659 long long id = PyLong_AsLongLong(requested_id);
1660 if (id == -1 && PyErr_Occurred() != NULL) {
1661 return NULL;
1662 }
1663 assert(id <= INT64_MAX);
1664 return _PyInterpreterState_LookUpID(id);
1665}
1666
1667static PyObject *
1668_get_id(PyInterpreterState *interp)
1669{
1670 PY_INT64_T id = PyInterpreterState_GetID(interp);
1671 if (id < 0) {
1672 return NULL;
1673 }
1674 return PyLong_FromLongLong(id);
1675}
1676
1677static int
1678_is_running(PyInterpreterState *interp)
1679{
1680 PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1681 if (PyThreadState_Next(tstate) != NULL) {
1682 PyErr_SetString(PyExc_RuntimeError,
1683 "interpreter has more than one thread");
1684 return -1;
1685 }
1686 PyFrameObject *frame = tstate->frame;
1687 if (frame == NULL) {
1688 if (PyErr_Occurred() != NULL) {
1689 return -1;
1690 }
1691 return 0;
1692 }
1693 return (int)(frame->f_executing);
1694}
1695
1696static int
1697_ensure_not_running(PyInterpreterState *interp)
1698{
1699 int is_running = _is_running(interp);
1700 if (is_running < 0) {
1701 return -1;
1702 }
1703 if (is_running) {
1704 PyErr_Format(PyExc_RuntimeError, "interpreter already running");
1705 return -1;
1706 }
1707 return 0;
1708}
1709
1710static int
1711_run_script(PyInterpreterState *interp, const char *codestr,
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001712 _sharedns *shared, _sharedexception **exc)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001713{
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001714 PyObject *exctype = NULL;
1715 PyObject *excval = NULL;
1716 PyObject *tb = NULL;
1717
Eric Snow7f8bfc92018-01-29 18:23:44 -07001718 PyObject *main_mod = PyMapping_GetItemString(interp->modules, "__main__");
1719 if (main_mod == NULL) {
1720 goto error;
1721 }
1722 PyObject *ns = PyModule_GetDict(main_mod); // borrowed
1723 Py_DECREF(main_mod);
1724 if (ns == NULL) {
1725 goto error;
1726 }
1727 Py_INCREF(ns);
1728
1729 // Apply the cross-interpreter data.
1730 if (shared != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001731 if (_sharedns_apply(shared, ns) != 0) {
1732 Py_DECREF(ns);
1733 goto error;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001734 }
1735 }
1736
1737 // Run the string (see PyRun_SimpleStringFlags).
1738 PyObject *result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL);
1739 Py_DECREF(ns);
1740 if (result == NULL) {
1741 goto error;
1742 }
1743 else {
1744 Py_DECREF(result); // We throw away the result.
1745 }
1746
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001747 *exc = NULL;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001748 return 0;
1749
1750error:
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001751 PyErr_Fetch(&exctype, &excval, &tb);
1752
1753 _sharedexception *sharedexc = _sharedexception_bind(exctype, excval, tb);
1754 Py_XDECREF(exctype);
1755 Py_XDECREF(excval);
1756 Py_XDECREF(tb);
1757 if (sharedexc == NULL) {
1758 fprintf(stderr, "RunFailedError: script raised an uncaught exception");
1759 PyErr_Clear();
1760 sharedexc = NULL;
1761 }
1762 else {
1763 assert(!PyErr_Occurred());
1764 }
1765 *exc = sharedexc;
Eric Snow7f8bfc92018-01-29 18:23:44 -07001766 return -1;
1767}
1768
1769static int
1770_run_script_in_interpreter(PyInterpreterState *interp, const char *codestr,
1771 PyObject *shareables)
1772{
1773 if (_ensure_not_running(interp) < 0) {
1774 return -1;
1775 }
1776
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001777 _sharedns *shared = _get_shared_ns(shareables);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001778 if (shared == NULL && PyErr_Occurred()) {
1779 return -1;
1780 }
1781
1782 // Switch to interpreter.
1783 PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
1784 PyThreadState *save_tstate = PyThreadState_Swap(tstate);
1785
1786 // Run the script.
1787 _sharedexception *exc = NULL;
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001788 int result = _run_script(interp, codestr, shared, &exc);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001789
1790 // Switch back.
1791 if (save_tstate != NULL) {
1792 PyThreadState_Swap(save_tstate);
1793 }
1794
1795 // Propagate any exception out to the caller.
1796 if (exc != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001797 _sharedexception_apply(exc, RunFailedError);
1798 _sharedexception_free(exc);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001799 }
1800 else if (result != 0) {
1801 // We were unable to allocate a shared exception.
1802 PyErr_NoMemory();
1803 }
1804
1805 if (shared != NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001806 _sharedns_free(shared);
Eric Snow7f8bfc92018-01-29 18:23:44 -07001807 }
1808
1809 return result;
1810}
1811
1812
1813/* module level code ********************************************************/
1814
1815/* globals is the process-global state for the module. It holds all
1816 the data that we need to share between interpreters, so it cannot
1817 hold PyObject values. */
1818static struct globals {
1819 _channels channels;
1820} _globals = {{0}};
1821
1822static int
1823_init_globals(void)
1824{
1825 if (_channels_init(&_globals.channels) != 0) {
1826 return -1;
1827 }
1828 return 0;
1829}
1830
1831static _channels *
1832_global_channels(void) {
1833 return &_globals.channels;
1834}
1835
1836static PyObject *
1837interp_create(PyObject *self, PyObject *args)
1838{
1839 if (!PyArg_UnpackTuple(args, "create", 0, 0)) {
1840 return NULL;
1841 }
1842
1843 // Create and initialize the new interpreter.
1844 PyThreadState *tstate, *save_tstate;
1845 save_tstate = PyThreadState_Swap(NULL);
1846 tstate = Py_NewInterpreter();
1847 PyThreadState_Swap(save_tstate);
1848 if (tstate == NULL) {
1849 /* Since no new thread state was created, there is no exception to
1850 propagate; raise a fresh one after swapping in the old thread
1851 state. */
1852 PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
1853 return NULL;
1854 }
1855 return _get_id(tstate->interp);
1856}
1857
1858PyDoc_STRVAR(create_doc,
1859"create() -> ID\n\
1860\n\
1861Create a new interpreter and return a unique generated ID.");
1862
1863
1864static PyObject *
1865interp_destroy(PyObject *self, PyObject *args)
1866{
1867 PyObject *id;
1868 if (!PyArg_UnpackTuple(args, "destroy", 1, 1, &id)) {
1869 return NULL;
1870 }
1871 if (!PyLong_Check(id)) {
1872 PyErr_SetString(PyExc_TypeError, "ID must be an int");
1873 return NULL;
1874 }
1875
1876 // Look up the interpreter.
1877 PyInterpreterState *interp = _look_up(id);
1878 if (interp == NULL) {
1879 return NULL;
1880 }
1881
1882 // Ensure we don't try to destroy the current interpreter.
1883 PyInterpreterState *current = _get_current();
1884 if (current == NULL) {
1885 return NULL;
1886 }
1887 if (interp == current) {
1888 PyErr_SetString(PyExc_RuntimeError,
1889 "cannot destroy the current interpreter");
1890 return NULL;
1891 }
1892
1893 // Ensure the interpreter isn't running.
1894 /* XXX We *could* support destroying a running interpreter but
1895 aren't going to worry about it for now. */
1896 if (_ensure_not_running(interp) < 0) {
1897 return NULL;
1898 }
1899
1900 // Destroy the interpreter.
1901 //PyInterpreterState_Delete(interp);
1902 PyThreadState *tstate, *save_tstate;
1903 tstate = PyInterpreterState_ThreadHead(interp);
1904 save_tstate = PyThreadState_Swap(tstate);
1905 Py_EndInterpreter(tstate);
1906 PyThreadState_Swap(save_tstate);
1907
1908 Py_RETURN_NONE;
1909}
1910
1911PyDoc_STRVAR(destroy_doc,
1912"destroy(ID)\n\
1913\n\
1914Destroy the identified interpreter.\n\
1915\n\
1916Attempting to destroy the current interpreter results in a RuntimeError.\n\
1917So does an unrecognized ID.");
1918
1919
1920static PyObject *
1921interp_list_all(PyObject *self)
1922{
1923 PyObject *ids, *id;
1924 PyInterpreterState *interp;
1925
1926 ids = PyList_New(0);
1927 if (ids == NULL) {
1928 return NULL;
1929 }
1930
1931 interp = PyInterpreterState_Head();
1932 while (interp != NULL) {
1933 id = _get_id(interp);
1934 if (id == NULL) {
1935 Py_DECREF(ids);
1936 return NULL;
1937 }
1938 // insert at front of list
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08001939 int res = PyList_Insert(ids, 0, id);
1940 Py_DECREF(id);
1941 if (res < 0) {
Eric Snow7f8bfc92018-01-29 18:23:44 -07001942 Py_DECREF(ids);
1943 return NULL;
1944 }
1945
1946 interp = PyInterpreterState_Next(interp);
1947 }
1948
1949 return ids;
1950}
1951
1952PyDoc_STRVAR(list_all_doc,
1953"list_all() -> [ID]\n\
1954\n\
1955Return a list containing the ID of every existing interpreter.");
1956
1957
1958static PyObject *
1959interp_get_current(PyObject *self)
1960{
1961 PyInterpreterState *interp =_get_current();
1962 if (interp == NULL) {
1963 return NULL;
1964 }
1965 return _get_id(interp);
1966}
1967
1968PyDoc_STRVAR(get_current_doc,
1969"get_current() -> ID\n\
1970\n\
1971Return the ID of current interpreter.");
1972
1973
1974static PyObject *
1975interp_get_main(PyObject *self)
1976{
1977 // Currently, 0 is always the main interpreter.
1978 return PyLong_FromLongLong(0);
1979}
1980
1981PyDoc_STRVAR(get_main_doc,
1982"get_main() -> ID\n\
1983\n\
1984Return the ID of main interpreter.");
1985
1986
1987static PyObject *
1988interp_run_string(PyObject *self, PyObject *args)
1989{
1990 PyObject *id, *code;
1991 PyObject *shared = NULL;
1992 if (!PyArg_UnpackTuple(args, "run_string", 2, 3, &id, &code, &shared)) {
1993 return NULL;
1994 }
1995 if (!PyLong_Check(id)) {
1996 PyErr_SetString(PyExc_TypeError, "first arg (ID) must be an int");
1997 return NULL;
1998 }
1999 if (!PyUnicode_Check(code)) {
2000 PyErr_SetString(PyExc_TypeError,
2001 "second arg (code) must be a string");
2002 return NULL;
2003 }
2004
2005 // Look up the interpreter.
2006 PyInterpreterState *interp = _look_up(id);
2007 if (interp == NULL) {
2008 return NULL;
2009 }
2010
2011 // Extract code.
2012 Py_ssize_t size;
2013 const char *codestr = PyUnicode_AsUTF8AndSize(code, &size);
2014 if (codestr == NULL) {
2015 return NULL;
2016 }
2017 if (strlen(codestr) != (size_t)size) {
2018 PyErr_SetString(PyExc_ValueError,
2019 "source code string cannot contain null bytes");
2020 return NULL;
2021 }
2022
2023 // Run the code in the interpreter.
2024 if (_run_script_in_interpreter(interp, codestr, shared) != 0) {
2025 return NULL;
2026 }
2027 Py_RETURN_NONE;
2028}
2029
2030PyDoc_STRVAR(run_string_doc,
2031"run_string(ID, sourcetext)\n\
2032\n\
2033Execute the provided string in the identified interpreter.\n\
2034\n\
2035See PyRun_SimpleStrings.");
2036
2037
2038static PyObject *
2039object_is_shareable(PyObject *self, PyObject *args)
2040{
2041 PyObject *obj;
2042 if (!PyArg_UnpackTuple(args, "is_shareable", 1, 1, &obj)) {
2043 return NULL;
2044 }
2045 if (_PyObject_CheckCrossInterpreterData(obj) == 0) {
2046 Py_RETURN_TRUE;
2047 }
2048 PyErr_Clear();
2049 Py_RETURN_FALSE;
2050}
2051
2052PyDoc_STRVAR(is_shareable_doc,
2053"is_shareable(obj) -> bool\n\
2054\n\
2055Return True if the object's data may be shared between interpreters and\n\
2056False otherwise.");
2057
2058
2059static PyObject *
2060interp_is_running(PyObject *self, PyObject *args)
2061{
2062 PyObject *id;
2063 if (!PyArg_UnpackTuple(args, "is_running", 1, 1, &id)) {
2064 return NULL;
2065 }
2066 if (!PyLong_Check(id)) {
2067 PyErr_SetString(PyExc_TypeError, "ID must be an int");
2068 return NULL;
2069 }
2070
2071 PyInterpreterState *interp = _look_up(id);
2072 if (interp == NULL) {
2073 return NULL;
2074 }
2075 int is_running = _is_running(interp);
2076 if (is_running < 0) {
2077 return NULL;
2078 }
2079 if (is_running) {
2080 Py_RETURN_TRUE;
2081 }
2082 Py_RETURN_FALSE;
2083}
2084
2085PyDoc_STRVAR(is_running_doc,
2086"is_running(id) -> bool\n\
2087\n\
2088Return whether or not the identified interpreter is running.");
2089
2090static PyObject *
2091channel_create(PyObject *self)
2092{
2093 int64_t cid = _channel_create(&_globals.channels);
2094 if (cid < 0) {
2095 return NULL;
2096 }
2097 PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0,
2098 &_globals.channels, 0);
2099 if (id == NULL) {
2100 if (_channel_destroy(&_globals.channels, cid) != 0) {
2101 // XXX issue a warning?
2102 }
2103 return NULL;
2104 }
2105 assert(((channelid *)id)->channels != NULL);
2106 return id;
2107}
2108
2109PyDoc_STRVAR(channel_create_doc,
2110"channel_create() -> ID\n\
2111\n\
2112Create a new cross-interpreter channel and return a unique generated ID.");
2113
2114static PyObject *
2115channel_destroy(PyObject *self, PyObject *args)
2116{
2117 PyObject *id;
2118 if (!PyArg_UnpackTuple(args, "channel_destroy", 1, 1, &id)) {
2119 return NULL;
2120 }
2121 int64_t cid = _coerce_id(id);
2122 if (cid < 0) {
2123 return NULL;
2124 }
2125
2126 if (_channel_destroy(&_globals.channels, cid) != 0) {
2127 return NULL;
2128 }
2129 Py_RETURN_NONE;
2130}
2131
2132PyDoc_STRVAR(channel_destroy_doc,
2133"channel_destroy(ID)\n\
2134\n\
2135Close and finalize the channel. Afterward attempts to use the channel\n\
2136will behave as though it never existed.");
2137
2138static PyObject *
2139channel_list_all(PyObject *self)
2140{
2141 int64_t count = 0;
2142 int64_t *cids = _channels_list_all(&_globals.channels, &count);
2143 if (cids == NULL) {
2144 if (count == 0) {
2145 return PyList_New(0);
2146 }
2147 return NULL;
2148 }
2149 PyObject *ids = PyList_New((Py_ssize_t)count);
2150 if (ids == NULL) {
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002151 goto finally;
Eric Snow7f8bfc92018-01-29 18:23:44 -07002152 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002153 int64_t *cur = cids;
2154 for (int64_t i=0; i < count; cur++, i++) {
2155 PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0,
Eric Snow7f8bfc92018-01-29 18:23:44 -07002156 &_globals.channels, 0);
2157 if (id == NULL) {
2158 Py_DECREF(ids);
2159 ids = NULL;
2160 break;
2161 }
2162 PyList_SET_ITEM(ids, i, id);
2163 }
Miss Islington (bot)f33eced2018-02-02 21:38:57 -08002164
2165finally:
2166 PyMem_Free(cids);
Eric Snow7f8bfc92018-01-29 18:23:44 -07002167 return ids;
2168}
2169
2170PyDoc_STRVAR(channel_list_all_doc,
2171"channel_list_all() -> [ID]\n\
2172\n\
2173Return the list of all IDs for active channels.");
2174
2175static PyObject *
2176channel_send(PyObject *self, PyObject *args)
2177{
2178 PyObject *id;
2179 PyObject *obj;
2180 if (!PyArg_UnpackTuple(args, "channel_send", 2, 2, &id, &obj)) {
2181 return NULL;
2182 }
2183 int64_t cid = _coerce_id(id);
2184 if (cid < 0) {
2185 return NULL;
2186 }
2187
2188 if (_channel_send(&_globals.channels, cid, obj) != 0) {
2189 return NULL;
2190 }
2191 Py_RETURN_NONE;
2192}
2193
2194PyDoc_STRVAR(channel_send_doc,
2195"channel_send(ID, obj)\n\
2196\n\
2197Add the object's data to the channel's queue.");
2198
2199static PyObject *
2200channel_recv(PyObject *self, PyObject *args)
2201{
2202 PyObject *id;
2203 if (!PyArg_UnpackTuple(args, "channel_recv", 1, 1, &id)) {
2204 return NULL;
2205 }
2206 int64_t cid = _coerce_id(id);
2207 if (cid < 0) {
2208 return NULL;
2209 }
2210
2211 return _channel_recv(&_globals.channels, cid);
2212}
2213
2214PyDoc_STRVAR(channel_recv_doc,
2215"channel_recv(ID) -> obj\n\
2216\n\
2217Return a new object from the data at the from of the channel's queue.");
2218
2219static PyObject *
2220channel_close(PyObject *self, PyObject *args, PyObject *kwds)
2221{
2222 PyObject *id;
2223 if (!PyArg_UnpackTuple(args, "channel_recv", 1, 1, &id)) {
2224 return NULL;
2225 }
2226 int64_t cid = _coerce_id(id);
2227 if (cid < 0) {
2228 return NULL;
2229 }
2230
2231 if (_channel_close(&_globals.channels, cid) != 0) {
2232 return NULL;
2233 }
2234 Py_RETURN_NONE;
2235}
2236
2237PyDoc_STRVAR(channel_close_doc,
2238"channel_close(ID)\n\
2239\n\
2240Close the channel for all interpreters. Once the channel's ID has\n\
2241no more ref counts the channel will be destroyed.");
2242
2243static PyObject *
2244channel_drop_interpreter(PyObject *self, PyObject *args, PyObject *kwds)
2245{
2246 // Note that only the current interpreter is affected.
Eric Snow83e64c82018-01-29 21:04:15 -07002247 static char *kwlist[] = {"id", "send", "recv", NULL};
Eric Snow7f8bfc92018-01-29 18:23:44 -07002248 PyObject *id;
2249 int send = -1;
2250 int recv = -1;
2251 if (!PyArg_ParseTupleAndKeywords(args, kwds,
2252 "O|$pp:channel_drop_interpreter", kwlist,
2253 &id, &send, &recv))
2254 return NULL;
2255
2256 int64_t cid = _coerce_id(id);
2257 if (cid < 0) {
2258 return NULL;
2259 }
2260 if (send < 0 && recv < 0) {
2261 send = 1;
2262 recv = 1;
2263 }
2264 else {
2265 if (send < 0) {
2266 send = 0;
2267 }
2268 if (recv < 0) {
2269 recv = 0;
2270 }
2271 }
2272 if (_channel_drop(&_globals.channels, cid, send, recv) != 0) {
2273 return NULL;
2274 }
2275 Py_RETURN_NONE;
2276}
2277
2278PyDoc_STRVAR(channel_drop_interpreter_doc,
2279"channel_drop_interpreter(ID, *, send=None, recv=None)\n\
2280\n\
2281Close the channel for the current interpreter. 'send' and 'recv'\n\
2282(bool) may be used to indicate the ends to close. By default both\n\
2283ends are closed. Closing an already closed end is a noop.");
2284
2285static PyObject *
2286channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
2287{
2288 return channelid_new(&ChannelIDtype, args, kwds);
2289}
2290
2291static PyMethodDef module_functions[] = {
2292 {"create", (PyCFunction)interp_create,
2293 METH_VARARGS, create_doc},
2294 {"destroy", (PyCFunction)interp_destroy,
2295 METH_VARARGS, destroy_doc},
2296 {"list_all", (PyCFunction)interp_list_all,
2297 METH_NOARGS, list_all_doc},
2298 {"get_current", (PyCFunction)interp_get_current,
2299 METH_NOARGS, get_current_doc},
2300 {"get_main", (PyCFunction)interp_get_main,
2301 METH_NOARGS, get_main_doc},
2302 {"is_running", (PyCFunction)interp_is_running,
2303 METH_VARARGS, is_running_doc},
2304 {"run_string", (PyCFunction)interp_run_string,
2305 METH_VARARGS, run_string_doc},
2306
2307 {"is_shareable", (PyCFunction)object_is_shareable,
2308 METH_VARARGS, is_shareable_doc},
2309
2310 {"channel_create", (PyCFunction)channel_create,
2311 METH_NOARGS, channel_create_doc},
2312 {"channel_destroy", (PyCFunction)channel_destroy,
2313 METH_VARARGS, channel_destroy_doc},
2314 {"channel_list_all", (PyCFunction)channel_list_all,
2315 METH_NOARGS, channel_list_all_doc},
2316 {"channel_send", (PyCFunction)channel_send,
2317 METH_VARARGS, channel_send_doc},
2318 {"channel_recv", (PyCFunction)channel_recv,
2319 METH_VARARGS, channel_recv_doc},
2320 {"channel_close", (PyCFunction)channel_close,
2321 METH_VARARGS, channel_close_doc},
2322 {"channel_drop_interpreter", (PyCFunction)channel_drop_interpreter,
2323 METH_VARARGS | METH_KEYWORDS, channel_drop_interpreter_doc},
2324 {"_channel_id", (PyCFunction)channel__channel_id,
2325 METH_VARARGS | METH_KEYWORDS, NULL},
2326
2327 {NULL, NULL} /* sentinel */
2328};
2329
2330
2331/* initialization function */
2332
2333PyDoc_STRVAR(module_doc,
2334"This module provides primitive operations to manage Python interpreters.\n\
2335The 'interpreters' module provides a more convenient interface.");
2336
2337static struct PyModuleDef interpretersmodule = {
2338 PyModuleDef_HEAD_INIT,
2339 "_xxsubinterpreters", /* m_name */
2340 module_doc, /* m_doc */
2341 -1, /* m_size */
2342 module_functions, /* m_methods */
2343 NULL, /* m_slots */
2344 NULL, /* m_traverse */
2345 NULL, /* m_clear */
2346 NULL /* m_free */
2347};
2348
2349
2350PyMODINIT_FUNC
2351PyInit__xxsubinterpreters(void)
2352{
2353 if (_init_globals() != 0) {
2354 return NULL;
2355 }
2356
2357 /* Initialize types */
2358 ChannelIDtype.tp_base = &PyLong_Type;
2359 if (PyType_Ready(&ChannelIDtype) != 0) {
2360 return NULL;
2361 }
2362
2363 /* Create the module */
2364 PyObject *module = PyModule_Create(&interpretersmodule);
2365 if (module == NULL) {
2366 return NULL;
2367 }
2368
2369 /* Add exception types */
2370 PyObject *ns = PyModule_GetDict(module); // borrowed
2371 if (interp_exceptions_init(ns) != 0) {
2372 return NULL;
2373 }
2374 if (channel_exceptions_init(ns) != 0) {
2375 return NULL;
2376 }
2377
2378 /* Add other types */
2379 Py_INCREF(&ChannelIDtype);
2380 if (PyDict_SetItemString(ns, "ChannelID", (PyObject *)&ChannelIDtype) != 0) {
2381 return NULL;
2382 }
2383
2384 if (_PyCrossInterpreterData_Register_Class(&ChannelIDtype, _channelid_shared)) {
2385 return NULL;
2386 }
2387
2388 return module;
2389}