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