New Socket Module

TableOfContents(3)

Introduction

This page descibes a new implementation of non-blocking sockets for jython. Hopefully, this new support should make possible the use of event-based server frameworks on jython. Examples of cpython event-based frameworks are [http://twistedmatrix.com/ Twisted], [http://www.zope.org/ Zope] and [http://www.amk.ca/python/code/medusa.html Medusa].

The implementation currently resides in the jython sandbox because the modules are new, and need testing on multiple Operating Systems and Java Virtual Machines (JVMs) before being committed to the core language.

Once we're satisfied that they are functional and reliable, they will be commited to the jython trunk, and will hopefully form part of the upcoming jython 2.2 release.

Requirements

Non-blocking support

SSL support

How to get these modules

The new socket and select modules have now been moved into the jython distribution, so any download of [http://www.jython.org/Project/download.html jython 2.2rc1] or more recent will contain the new modules.

How to use these modules

As before, the socket module is imported like this

import socket

The new socket module has been written to comply as closely as possible with the cpython 2.4 API for non-blocking sockets, therefore you should use the cpython socket documentation as your reference when writing code.

http://www.python.org/doc/2.4/lib/module-socket.html

If the new jython module exhibits behaviour that differs from that described in the cpython documentation, then that should be considered a bug and reported as such.

SSL Support

These modules include an implementation of client side SSL support, which is compatible with the cpython SSL API. The client side ssl example from the cpython documentation should just work.

http://docs.python.org/lib/socket-example.html

However, the cpython SSL API is extremely basic, and essentially only permits the formation of SSL wrapped sockets. It does NOT include support for any of the following

All of the above are possible, but since no other python version includes that support in the base distribution, I'm not going to do it for jython either; trying to design an API would complex enough; implementing would be a lot of work beyond that.

If you have serious SSL or crypto requirements, then I strongly recommend using the java crypto libraries, or one of the excellent third-party crypto libraries for java, such as that from the [http://www.bouncycastle.org Legion of the Bouncy Castle].

Jython versions

These modules were developed on jython 2.1. They were tested on all available later versions, which at this time is [http://www.jython.org/Project/download.html 2.2rc1] and 2.3 (a0?).

The modules should work identically on all versions, and have passed all available unit tests on all versions.

Cpython versions

It is the intention that these modules be fully API compatible with cpython, as far as is possible or sensible. This means that any cpython socket code that is syntax compatible with your selected jython version should produce identical behaviour to the same code running on cpython.

The unit-tests provided with these modules should also run on all versions of cpython. See below under unit-tests for more details.

JVM bug fixes

The java.nio package was introduced in java in 200?. This implementation of non-blocking sockets for jython was written in 2004, and was essentially complete back then.

However, whenever it was run against JVMs available at that time, and on JVMs up to and including version 1.4.2_09 (release date), it would hang when the unit tests were run (On Windows Server 2003 and Windows 2000 Server). The main symptom was that server sockets would remain in existence long after they had been closed, in CLOSE_WAIT and TIME_WAIT states which would time out after a long period of minutes.

I tore my hair out for a long time trying to figure out what the problem was, and tried all kinds of approaches to fix the problem, i.e. adding, omitting or changing the various operations involved in closing a non-blocking socket. You can read more detail here

http://www.nabble.com/Non-blocking-IO-update.-tf389917.html#a1074623

But when I upgraded my JVM to jdk 1.4.2_13, and ran the tests on that, the whole problem disappeared! I still do not know why; possibilities include

But you should know that if you use this non-blocking support on JVM versions prior to 1.4.2_13, it is quite possible that you will have the same problem with sockets not closing properly. Therefore, I recommend running this code only on JDK 1.4.2_13 or greater; if you successfully run it on previous versions, especially on different platforms, let me know about.

Other I/O

The design of the cpython non-blocking API is derived from the UNIX C api, which deals with FILE DESCRIPTORS. Since file descriptors can describe any type of I/O channel on unix OSes, cpython can deal with selecting and polling on multiple channel types, such non-blocking files, pipes and named-pipes, fifos, etc.

The java model for selecting on channels is much more restrictive. There is a java abstract class, java.nio.channels.SelectableChannel, which other channel classes must subclass if they are to be multiplexed. On 1.4 JVMs, only the following classes subclass [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/SelectableChannel.html SelectableChannel].

Specifically, [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/FileChannel.html FileChannel's] do not subclass [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/SelectableChannel.html SelectableChannel], and thus it is not possible to include files in non-blocking multiplex operations on Java platforms.

Of the two channel types listed above, these modules only support socket channels. This is because it was necessary to rewrite all of the socket creation calls to return SelectableChannels. To do so for Pipes, which are used for communication with sub-processes, it would be necessary to rewrite the jython sub-process creation modules, i.e. popen, etc, to create SelectableChannels. Although it should be reasonably straightforward to implement this, I have no plans to do this work.

Unit tests

If you plan to make use of these modules on any platform, I highly recommend running the unit tests first; they should flush out any problems you might have.

There are three test modules in the test subdirectory

  1. test_socket.py. This module is ported from the cpython 2.4 code base; all tests in it pass on all available jython versions from 2.1 on. The ported module still runs on cpython, for compatibility checks. (Actually, it fixes a couple of small bugs in the original cpython test module).

  2. test_select.py. This module is ported from the cpython 2.4 code base, but rewritten so that they work with the unittest module. All tests in it pass on all available jython versions from 2.1 on. The ported module still runs on cpython, for compatibility checks.

  3. test_select_new.py. This is a brand new module that I wrote to test the select API. It has been written to work on cpython and jython, and passes all tests on both.

Differences between cpython and jython

Error handling

These modules have been coded so that the error handling is as close to the error handling of cpython as possible. So, ideally, cpython socket and select code run on jython should exhibit identical behaviour to cpython, in terms of exception types raised, error numbers returned, etc.

However, due to the different semantics of java and C, there will be differences in the error handling, which will be documented here as they are discovered.

Differences in the treatment of zero timeout values

On cpython, when you specify a zero (i.e. 0 or 0.0) timeout value, the socket should behave the same as if the socket had been placed in non-blocking mode. See the [http://www.python.org/doc/lib/socket-objects.html cpython socket object documentation] for details.

However, [http://java.sun.com/j2se/1.4.2/docs/api/java/net/Socket.html#setSoTimeout(int) Java interprets a zero timeout value as an infinite timeout], i.e. the socket is placed in blocking mode.

To solve this conflict, I decided that the best thing to do with zero timeouts is to adjust them to the smallest possible timeout in java, which is 1 millisecond. So if you do socket.settimeout(0) with the new jython socket module, what you will really get is equivalent to the cpython call socket.settimeout(0.001).

This means that you may get differing exception signatures when using zero timeouts under cpython and jython.

  1. Cpython: socket operations with zero timeouts that fail will raise socket.error exceptions. This is equivalent to a -1 return from the C socket.recv call, with errno set to EAGAIN, meaning "The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received."
  2. Jython: socket operations with zero timeouts that fail will generate socket.timeout exceptions.

Known issues and workarounds

Deadlock problems with socket.makefile()

Description

This is a bug that has been reported on the [http://sourceforge.net/tracker/index.php?func=detail&aid=1744567&group_id=12867&atid=112867 bug tracker].

If the user

  1. Opens a socket
  2. Obtains a file wrapper for that socket, using the makefile() method
  3. Tries to simultaneously read and write on the file wrapper from different threads
  4. A deadlock will ensue

Cause

This is because of the way that the InputStream and OutputStream for a socket are created under java.nio, i.e. by static methods on the [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/Channels.html java.nio.channels.Channels] class. As you can see from these bug reports on the java bug database

There is a lock shared between the resulting InputStream and OutputStream that prevents simultaneous read and write. If you do attempt to simultaneously read and write on the same socket/file from two different threads, you risk getting a deadlock.

Fix

Since the java bug report is nearly six years old, it is reasonable to assume that it will not be fixed promptly by Sun. There is a possible implementation pathway that can get around this problem, by creating a PyFile that can map read and write operations to channels instead of streams.

However, the viability of this fix is unknown, and requires further research.

Workaround

Until a fix is delivered, there are several workarounds to the problem.

  1. Do not use file wrappers around sockets. Instead, use the socket.send and socket.recv methods, which are not affected by this lock. In order to minimise code change, you need only adopt this strategy for reading or writing, but not both. So long as either reading-only or writing-only is carried out through the file wrapper, there should no lock contention or deadlock.
  2. Use the select.select call to multiplex channels. The select.select function and select.poll objects have recently been added to jython; try them out.
  3. Ensure that your code does not try to read and write simultaneously on the same socket file wrapper. You can do this by implementing your own exclusion (e.g. a semaphore), or perhaps using delays to ensure that the read and write operations happen at different times, or by restricting all read and write operations on a socket file wrapper to a single thread.