forked from IronLanguages/ironpython3
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathselect.cs
More file actions
137 lines (118 loc) · 6.53 KB
/
select.cs
File metadata and controls
137 lines (118 loc) · 6.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
#if FEATURE_SYNC_SOCKETS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using IronPython.Runtime;
using IronPython.Runtime.Exceptions;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
using Microsoft.Scripting.Runtime;
[assembly: PythonModule("select", typeof(IronPython.Modules.PythonSelect))]
namespace IronPython.Modules {
public static class PythonSelect {
public const string __doc__ = "Provides support for asynchronous socket operations.";
public static PythonType error => PythonExceptions.OSError;
#region Public API
[Documentation("select(iwtd, owtd, ewtd[, timeout]) -> readlist, writelist, errlist\n\n"
+ "Block until sockets are available for reading or writing, until an error\n"
+ "occurs, or until a the timeout expires. The first three parameters are\n"
+ "sequences of socket objects (opened using the socket module). The last is a\n"
+ "timeout value, given in seconds as a float. If timeout is omitted, select()\n"
+ "blocks until at least one socket is ready. A timeout of zero never blocks, but\n"
+ "can be used for polling.\n"
+ "\n"
+ "The return value is a tuple of lists of sockets that are ready (subsets of\n"
+ "iwtd, owtd, and ewtd). If the timeout occurs before any sockets are ready, a\n"
+ "tuple of three empty lists is returned.\n"
+ "\n"
+ "Note that select() on IronPython works only with sockets; it will not work with\n"
+ "files or other objects."
)]
public static PythonTuple select(CodeContext/*!*/ context, object iwtd, object owtd, object ewtd, object timeout=null) {
PythonList readerList, writerList, errorList;
Dictionary<Socket, object> readerOriginals, writerOriginals, errorOriginals;
ProcessSocketSequence(context, iwtd, out readerList, out readerOriginals);
ProcessSocketSequence(context, owtd, out writerList, out writerOriginals);
ProcessSocketSequence(context, ewtd, out errorList, out errorOriginals);
int timeoutMicroseconds;
if (timeout == null) {
// -1 doesn't really work as infinite, but it appears that any other negative value does
timeoutMicroseconds = -2;
} else {
double timeoutSeconds;
if (!Converter.TryConvertToDouble(timeout, out timeoutSeconds)) {
throw PythonOps.TypeErrorForTypeMismatch("float or None", timeout);
}
timeoutMicroseconds = (int) (1000000 * timeoutSeconds);
}
try {
Socket.Select(readerList, writerList, errorList, timeoutMicroseconds);
} catch (ArgumentNullException) {
throw PythonSocket.MakeException(context, new SocketException((int)SocketError.InvalidArgument));
} catch (SocketException e) {
throw PythonSocket.MakeException(context, e);
}
// Convert back to what the user originally passed in
for (int i = 0; i < readerList.__len__(); i++) readerList[i] = readerOriginals[(Socket)readerList[i]];
for (int i = 0; i < writerList.__len__(); i++) writerList[i] = writerOriginals[(Socket)writerList[i]];
for (int i = 0; i < errorList.__len__(); i++) errorList[i] = errorOriginals[(Socket)errorList[i]];
return PythonTuple.MakeTuple(readerList, writerList, errorList);
}
/// <summary>
/// Process a sequence of objects that are compatible with ObjectToSocket(). Return two
/// things as out params: an in-order List of sockets that correspond to the original
/// objects in the passed-in sequence, and a mapping of these socket objects to their
/// original objects.
///
/// The socketToOriginal mapping is generated because the CPython select module supports
/// passing to select either file descriptor numbers or an object with a fileno() method.
/// We try to be faithful to what was originally requested when we return.
/// </summary>
private static void ProcessSocketSequence(CodeContext context, object sequence, out PythonList socketList, out Dictionary<Socket, object> socketToOriginal) {
socketToOriginal = new Dictionary<Socket, object>();
socketList = new PythonList();
IEnumerator cursor = PythonOps.GetEnumerator(sequence);
while (cursor.MoveNext()) {
object original = cursor.Current;
Socket socket = ObjectToSocket(context, original);
socketList.append(socket);
socketToOriginal[socket] = original;
}
}
/// <summary>
/// Return the System.Net.Sockets.Socket object that corresponds to the passed-in
/// object. obj can be a System.Net.Sockets.Socket, a PythonSocket.SocketObj, a
/// long integer (representing a socket handle), or a Python object with a fileno()
/// method (whose result is used to look up an existing PythonSocket.SocketObj,
/// which is in turn converted to a Socket.
/// </summary>
private static Socket ObjectToSocket(CodeContext context, object obj) {
Socket socket;
PythonSocket.socket pythonSocket = obj as PythonSocket.socket;
if (pythonSocket != null) {
return pythonSocket._socket;
}
Int64 handle;
if (!Converter.TryConvertToInt64(obj, out handle)) {
object userSocket = obj;
object filenoCallable = PythonOps.GetBoundAttr(context, userSocket, "fileno");
object fileno = PythonCalls.Call(context, filenoCallable);
handle = Converter.ConvertToInt64(fileno);
}
if (handle < 0) {
throw PythonOps.ValueError("file descriptor cannot be a negative number ({0})", handle);
}
socket = PythonSocket.socket.HandleToSocket(handle);
if (socket == null) {
throw PythonSocket.MakeException(context, new SocketException((int)SocketError.NotSocket));
}
return socket;
}
#endregion
}
}
#endif