PageRenderTime 150ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_1_1/Src/IronPython/Modules/select.cs

#
C# | 156 lines | 104 code | 19 blank | 33 comment | 14 complexity | acd7bc5d11ae15b7589b19530e050781 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public
  6. * License. A copy of the license can be found in the License.html file at the
  7. * root of this distribution. If you cannot locate the Microsoft Public
  8. * License, please send an email to dlr@microsoft.com. By using this source
  9. * code in any fashion, you are agreeing to be bound by the terms of the
  10. * Microsoft Public License.
  11. *
  12. * You must not remove this notice, or any other, from this software.
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Net.Sockets;
  19. using System.Runtime.InteropServices;
  20. using System.Text;
  21. using IronMath;
  22. using IronPython.Runtime;
  23. using IronPython.Runtime.Calls;
  24. using IronPython.Runtime.Exceptions;
  25. using IronPython.Runtime.Operations;
  26. [assembly: PythonModule("select", typeof(IronPython.Modules.PythonSelect))]
  27. namespace IronPython.Modules {
  28. [Documentation("Provides support for asynchronous socket operations.")]
  29. public static class PythonSelect {
  30. #region Public API
  31. public static IPythonType error = ExceptionConverter.CreatePythonException("error", "select");
  32. [Documentation("select(iwtd, owtd, ewtd[, timeout]) -> readlist, writelist, errlist\n\n"
  33. + "Block until sockets are available for reading or writing, until an error\n"
  34. + "occurs, or until a the timeout expires. The first three parameters are\n"
  35. + "sequences of socket objects (opened using the socket module). The last is a\n"
  36. + "timeout value, given in seconds as a float. If timeout is omitted, select()\n"
  37. + "blocks until at least one socket is ready. A timeout of zero never blocks, but\n"
  38. + "can be used for polling.\n"
  39. + "\n"
  40. + "The return value is a tuple of lists of sockets that are ready (subsets of\n"
  41. + "iwtd, owtd, and ewtd). If the timeout occurs before any sockets are ready, a\n"
  42. + "tuple of three empty lists is returned.\n"
  43. + "\n"
  44. + "Note that select() on IronPython works only with sockets; it will not work with\n"
  45. + "files or other objects."
  46. )]
  47. [PythonName("select")]
  48. public static Tuple Select(object iwtd, object owtd, object ewtd, [DefaultParameterValue(null)] object timeout) {
  49. List readerList, writerList, errorList;
  50. Dictionary<Socket, object> readerOriginals, writerOriginals, errorOriginals;
  51. ProcessSocketSequence(iwtd, out readerList, out readerOriginals);
  52. ProcessSocketSequence(owtd, out writerList, out writerOriginals);
  53. ProcessSocketSequence(ewtd, out errorList, out errorOriginals);
  54. int timeoutMicroseconds;
  55. if (timeout == null) {
  56. // -1 doesn't really work as infinite, but it appears that any other negative value does
  57. timeoutMicroseconds = -2;
  58. } else {
  59. double timeoutSeconds;
  60. if (!Converter.TryConvertToDouble(timeout, out timeoutSeconds)) {
  61. throw Ops.TypeErrorForTypeMismatch("float or None", timeout);
  62. }
  63. timeoutMicroseconds = (int) (1000000 * timeoutSeconds);
  64. }
  65. try {
  66. Socket.Select(readerList, writerList, errorList, timeoutMicroseconds);
  67. } catch (ArgumentNullException) {
  68. throw MakeException(SocketExceptionToTuple(new SocketException((int)SocketError.InvalidArgument)));
  69. } catch (SocketException e) {
  70. throw MakeException(SocketExceptionToTuple(e));
  71. }
  72. // Convert back to what the user originally passed in
  73. for (int i = 0; i < readerList.Count; i++) readerList[i] = readerOriginals[(Socket)readerList[i]];
  74. for (int i = 0; i < writerList.Count; i++) writerList[i] = writerOriginals[(Socket)writerList[i]];
  75. for (int i = 0; i < errorList.Count; i++) errorList[i] = errorOriginals[(Socket)errorList[i]];
  76. return Tuple.MakeTuple(readerList, writerList, errorList);
  77. }
  78. private static Tuple SocketExceptionToTuple(SocketException e) {
  79. return Tuple.MakeTuple(e.ErrorCode, e.Message);
  80. }
  81. private static Exception MakeException(object value) {
  82. return ExceptionConverter.CreateThrowable(error, value);
  83. }
  84. /// <summary>
  85. /// Process a sequence of objects that are compatible with ObjectToSocket(). Return two
  86. /// things as out params: an in-order List of sockets that correspond to the original
  87. /// objects in the passed-in sequence, and a mapping of these socket objects to their
  88. /// original objects.
  89. ///
  90. /// The socketToOriginal mapping is generated because the CPython select module supports
  91. /// passing to select either file descriptor numbers or an object with a fileno() method.
  92. /// We try to be faithful to what was originally requested when we return.
  93. /// </summary>
  94. private static void ProcessSocketSequence(object sequence, out List socketList, out Dictionary<Socket, object> socketToOriginal) {
  95. socketToOriginal = new Dictionary<Socket, object>();
  96. socketList = new List();
  97. IEnumerator cursor = Ops.GetEnumerator(sequence);
  98. while (cursor.MoveNext()) {
  99. object original = cursor.Current;
  100. Socket socket = ObjectToSocket(original);
  101. socketList.Add(socket);
  102. socketToOriginal[socket] = original;
  103. }
  104. }
  105. /// <summary>
  106. /// Return the System.Net.Sockets.Socket object that corresponds to the passed-in
  107. /// object. obj can be a System.Net.Sockets.Socket, a PythonSocket.SocketObj, a
  108. /// long integer (representing a socket handle), or a Python object with a fileno()
  109. /// method (whose result is used to look up an existing PythonSocket.SocketObj,
  110. /// which is in turn converted to a Socket.
  111. /// </summary>
  112. private static Socket ObjectToSocket(object obj) {
  113. Socket socket;
  114. PythonSocket.SocketObj pythonSocket = obj as PythonSocket.SocketObj;
  115. if (pythonSocket != null) {
  116. return pythonSocket.socket;
  117. }
  118. Int64 handle;
  119. if (!Converter.TryConvertToInt64(obj, out handle)) {
  120. object userSocket = obj;
  121. object filenoCallable = Ops.GetAttr(DefaultContext.Default, userSocket, SymbolTable.StringToId("fileno"));
  122. object fileno = Ops.Call(filenoCallable);
  123. handle = Converter.ConvertToInt64(fileno);
  124. }
  125. if (handle < 0) {
  126. throw Ops.ValueError("file descriptor cannot be a negative number ({0})", handle);
  127. }
  128. socket = PythonSocket.SocketObj.HandleToSocket(handle);
  129. if (socket == null) {
  130. SocketException e = new SocketException((int)SocketError.NotSocket);
  131. throw ExceptionConverter.CreateThrowable(error, Tuple.MakeTuple(e.ErrorCode, e.Message));
  132. }
  133. return socket;
  134. }
  135. #endregion
  136. }
  137. }