Skip to content

Commit 7bd560c

Browse files
gh-76785: Add SendChannel.send_buffer() (#110246)
(This is still a test module.)
1 parent f4cb0d2 commit 7bd560c

13 files changed

+467
-67
lines changed

Include/internal/pycore_ceval.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,26 @@ extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock);
4141
extern void _PyEval_FiniState(struct _ceval_state *ceval);
4242
extern void _PyEval_SignalReceived(PyInterpreterState *interp);
4343

44+
// bitwise flags:
45+
#define _Py_PENDING_MAINTHREADONLY 1
46+
#define _Py_PENDING_RAWFREE 2
47+
4448
// Export for '_testinternalcapi' shared extension
4549
PyAPI_FUNC(int) _PyEval_AddPendingCall(
4650
PyInterpreterState *interp,
4751
_Py_pending_call_func func,
4852
void *arg,
49-
int mainthreadonly);
53+
int flags);
54+
55+
typedef int (*_Py_simple_func)(void *);
56+
extern int _Py_CallInInterpreter(
57+
PyInterpreterState *interp,
58+
_Py_simple_func func,
59+
void *arg);
60+
extern int _Py_CallInInterpreterAndRawFree(
61+
PyInterpreterState *interp,
62+
_Py_simple_func func,
63+
void *arg);
5064

5165
extern void _PyEval_SignalAsyncExc(PyInterpreterState *interp);
5266
#ifdef HAVE_FORK

Include/internal/pycore_ceval_state.h

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct _pending_calls {
2222
struct _pending_call {
2323
_Py_pending_call_func func;
2424
void *arg;
25+
int flags;
2526
} calls[NPENDINGCALLS];
2627
int first;
2728
int last;

Include/internal/pycore_interp.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ _PyInterpreterState_SetFinalizing(PyInterpreterState *interp, PyThreadState *tst
267267
}
268268

269269

270-
extern PyInterpreterState* _PyInterpreterState_LookUpID(int64_t);
270+
// Export for the _xxinterpchannels module.
271+
PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_LookUpID(int64_t);
271272

272273
extern int _PyInterpreterState_IDInitref(PyInterpreterState *);
273274
extern int _PyInterpreterState_IDIncref(PyInterpreterState *);

Include/internal/pycore_pybuffer.h

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef Py_INTERNAL_PYBUFFER_H
2+
#define Py_INTERNAL_PYBUFFER_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
12+
// Exported for the _xxinterpchannels module.
13+
PyAPI_FUNC(int) _PyBuffer_ReleaseInInterpreter(
14+
PyInterpreterState *interp, Py_buffer *view);
15+
PyAPI_FUNC(int) _PyBuffer_ReleaseInInterpreterAndRawFree(
16+
PyInterpreterState *interp, Py_buffer *view);
17+
18+
#ifdef __cplusplus
19+
}
20+
#endif
21+
#endif /* !Py_INTERNAL_PYBUFFER_H */

Lib/test/support/interpreters.py

+15
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,21 @@ def send_nowait(self, obj):
225225
# See bpo-32604 and gh-19829.
226226
return _channels.send(self._id, obj)
227227

228+
def send_buffer(self, obj):
229+
"""Send the object's buffer to the channel's receiving end.
230+
231+
This blocks until the object is received.
232+
"""
233+
_channels.send_buffer(self._id, obj)
234+
235+
def send_buffer_nowait(self, obj):
236+
"""Send the object's buffer to the channel's receiving end.
237+
238+
If the object is immediately received then return True
239+
(else False). Otherwise this is the same as send().
240+
"""
241+
return _channels.send_buffer(self._id, obj)
242+
228243
def close(self):
229244
_channels.close(self._id, send=True)
230245

Lib/test/test__xxinterpchannels.py

+15
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,21 @@ def test_recv_sending_interp_destroyed(self):
703703
channels.recv(cid2)
704704
del cid2
705705

706+
def test_send_buffer(self):
707+
buf = bytearray(b'spamspamspam')
708+
cid = channels.create()
709+
channels.send_buffer(cid, buf)
710+
obj = channels.recv(cid)
711+
712+
self.assertIsNot(obj, buf)
713+
self.assertIsInstance(obj, memoryview)
714+
self.assertEqual(obj, buf)
715+
716+
buf[4:8] = b'eggs'
717+
self.assertEqual(obj, buf)
718+
obj[4:8] = b'ham.'
719+
self.assertEqual(obj, buf)
720+
706721
def test_allowed_types(self):
707722
cid = channels.create()
708723
objects = [

Lib/test/test_interpreters.py

+43
Original file line numberDiff line numberDiff line change
@@ -1067,3 +1067,46 @@ def test_recv_nowait_default(self):
10671067
self.assertEqual(obj4, b'spam')
10681068
self.assertEqual(obj5, b'eggs')
10691069
self.assertIs(obj6, default)
1070+
1071+
def test_send_buffer(self):
1072+
buf = bytearray(b'spamspamspam')
1073+
obj = None
1074+
rch, sch = interpreters.create_channel()
1075+
1076+
def f():
1077+
nonlocal obj
1078+
while True:
1079+
try:
1080+
obj = rch.recv()
1081+
break
1082+
except interpreters.ChannelEmptyError:
1083+
time.sleep(0.1)
1084+
t = threading.Thread(target=f)
1085+
t.start()
1086+
1087+
sch.send_buffer(buf)
1088+
t.join()
1089+
1090+
self.assertIsNot(obj, buf)
1091+
self.assertIsInstance(obj, memoryview)
1092+
self.assertEqual(obj, buf)
1093+
1094+
buf[4:8] = b'eggs'
1095+
self.assertEqual(obj, buf)
1096+
obj[4:8] = b'ham.'
1097+
self.assertEqual(obj, buf)
1098+
1099+
def test_send_buffer_nowait(self):
1100+
buf = bytearray(b'spamspamspam')
1101+
rch, sch = interpreters.create_channel()
1102+
sch.send_buffer_nowait(buf)
1103+
obj = rch.recv()
1104+
1105+
self.assertIsNot(obj, buf)
1106+
self.assertIsInstance(obj, memoryview)
1107+
self.assertEqual(obj, buf)
1108+
1109+
buf[4:8] = b'eggs'
1110+
self.assertEqual(obj, buf)
1111+
obj[4:8] = b'ham.'
1112+
self.assertEqual(obj, buf)

Makefile.pre.in

+1
Original file line numberDiff line numberDiff line change
@@ -1791,6 +1791,7 @@ PYTHON_HEADERS= \
17911791
$(srcdir)/Include/internal/pycore_parking_lot.h \
17921792
$(srcdir)/Include/internal/pycore_pathconfig.h \
17931793
$(srcdir)/Include/internal/pycore_pyarena.h \
1794+
$(srcdir)/Include/internal/pycore_pybuffer.h \
17941795
$(srcdir)/Include/internal/pycore_pyerrors.h \
17951796
$(srcdir)/Include/internal/pycore_pyhash.h \
17961797
$(srcdir)/Include/internal/pycore_pylifecycle.h \

0 commit comments

Comments
 (0)