* Makefile: Added new targets for running the tests. Now the generated
[mono.git] / mcs / class / System / System.Net.Sockets / Socket.cs
index c5b08928ade95886b1bfd612dbe47648f80411fd..bb93fcd57b2ff6e9ecc24ed2abe076ffb3550e46 100644 (file)
@@ -1,17 +1,20 @@
 // System.Net.Sockets.Socket.cs
 //
 // Authors:
-//    Phillip Pearson (pp@myelin.co.nz)
-//    Dick Porter <dick@ximian.com>
+//     Phillip Pearson (pp@myelin.co.nz)
+//     Dick Porter <dick@ximian.com>
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //
 // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc.
 //    http://www.myelin.co.nz
+// (c) 2004 Novell, Inc. (http://www.novell.com)
 //
 
 using System;
 using System.Net;
 using System.Collections;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Threading;
 using System.Reflection;
 using System.IO;
@@ -20,38 +23,102 @@ namespace System.Net.Sockets
 {
        public class Socket : IDisposable 
        {
+               [StructLayout (LayoutKind.Sequential)]
                private sealed class SocketAsyncResult: IAsyncResult 
                {
-                       private object state;
-                       private WaitHandle waithandle;
-                       private bool completed_sync, completed;
-                       private Worker worker;
-                       private Exception delayedException = null;
+                       /* Same structure in the runtime */
+                       public Socket Sock;
+                       IntPtr handle;
+                       object state;
+                       AsyncCallback callback;
+                       WaitHandle waithandle;
 
-                       public SocketAsyncResult(object state) {
-                               this.state=state;
-                               waithandle=new ManualResetEvent(false);
-                               completed_sync=completed=false;
+                       Exception delayedException;
+
+                       public EndPoint EndPoint;       // Connect,ReceiveFrom,SendTo
+                       public byte [] Buffer;          // Receive,ReceiveFrom,Send,SendTo
+                       public int Offset;              // Receive,ReceiveFrom,Send,SendTo
+                       public int Size;                // Receive,ReceiveFrom,Send,SendTo
+                       public SocketFlags SockFlags;   // Receive,ReceiveFrom,Send,SendTo
+
+                       // Return values
+                       Socket acc_socket;
+                       int total;
+
+                       bool completed_sync;
+                       bool completed;
+                       AsyncCallback real_callback;
+                       int error;
+
+                       public SocketAsyncResult (Socket sock, object state, AsyncCallback callback)
+                       {
+                               this.Sock = sock;
+                               this.handle = sock.socket;
+                               this.state = state;
+                               this.real_callback = callback;
+                               SockFlags = SocketFlags.None;
                        }
 
-                       public void SetDelayedException (Exception e) {
-                               delayedException = e;
+                       public void CreateAsyncDelegate ()
+                       {
+                               if (real_callback != null)
+                                       this.callback = new AsyncCallback (FakeCB);
+                       }
+
+                       static void FakeCB (IAsyncResult result)
+                       {
+                               SocketAsyncResult ares = (SocketAsyncResult) result;
+                               ares.real_callback.BeginInvoke (ares, null, null);
                        }
 
-                       public void CheckIfThrowDelayedException () {
+                       public void CheckIfThrowDelayedException ()
+                       {
                                if (delayedException != null)
                                        throw delayedException;
+
+                               if (error != 0)
+                                       throw new SocketException (error);
+                       }
+
+                       public void Complete ()
+                       {
+                               IsCompleted = true;
+                               if (real_callback != null)
+                                       real_callback (this);
+                       }
+
+                       public void Complete (int total)
+                       {
+                               this.total = total;
+                               Complete ();
+                       }
+                       
+                       public void Complete (Exception e)
+                       {
+                               delayedException = e;
+                               Complete ();
+                       }
+
+                       public void Complete (Socket s)
+                       {
+                               acc_socket = s;
+                               Complete ();
                        }
 
                        public object AsyncState {
                                get {
-                                       return(state);
+                                       return state;
                                }
                        }
 
                        public WaitHandle AsyncWaitHandle {
                                get {
-                                       return(waithandle);
+                                       lock (this) {
+                                               if (waithandle == null)
+                                                       waithandle = new ManualResetEvent (completed);
+                                       }
+
+                                       return waithandle;
                                }
                                set {
                                        waithandle=value;
@@ -70,224 +137,160 @@ namespace System.Net.Sockets
                                }
                                set {
                                        completed=value;
+                                       lock (this) {
+                                               if (waithandle != null && value) {
+                                                       ((ManualResetEvent) waithandle).Set ();
+                                               }
+                                       }
                                }
                        }
                        
-                       public Worker Worker {
+                       public Socket Socket {
                                get {
-                                       return(worker);
+                                       return acc_socket;
                                }
-                               set {
-                                       worker=value;
+                       }
+
+                       public int Total {
+                               get {
+                                       return total;
                                }
                        }
                }
 
                private sealed class Worker 
                {
-                       private AsyncCallback callback;
-                       private SocketAsyncResult result;
-                       private Socket socket;
-
-                       // Parameters
-                       private EndPoint endpoint;      // Connect,ReceiveFrom,SendTo
-                       private byte[] buffer;          // Receive,ReceiveFrom,Send,SendTo
-                       private int offset;             // Receive,ReceiveFrom,Send,SendTo
-                       private int size;               // Receive,ReceiveFrom,Send,SendTo
-                       private SocketFlags sockflags;  // Receive,ReceiveFrom,Send,SendTo
+                       SocketAsyncResult result;
 
-                       // Return values
-                       private Socket acc_socket;
-                       private int total;
-                       
-
-                       // For Accept
-                       public Worker(Socket req_sock,
-                                     AsyncCallback req_callback,
-                                     SocketAsyncResult req_result)
-                               : this(req_sock, null, 0, 0, SocketFlags.None,
-                                      null, req_callback, req_result) {}
-
-                       // For Connect
-                       public Worker(Socket req_sock, EndPoint req_endpoint,
-                                     AsyncCallback req_callback,
-                                     SocketAsyncResult req_result)
-                               : this(req_sock, null, 0, 0, SocketFlags.None,
-                                      req_endpoint, req_callback,
-                                      req_result) {}
-
-                       // For Receive and Send
-                       public Worker(Socket req_sock, byte[] req_buffer,
-                                     int req_offset, int req_size,
-                                     SocketFlags req_sockflags,
-                                     AsyncCallback req_callback,
-                                     SocketAsyncResult req_result)
-                               : this(req_sock, req_buffer, req_offset,
-                                      req_size, req_sockflags, null,
-                                      req_callback, req_result) {}
-
-                       // For ReceiveFrom and SendTo
-                       public Worker(Socket req_sock, byte[] req_buffer,
-                                     int req_offset, int req_size,
-                                     SocketFlags req_sockflags,
-                                     EndPoint req_endpoint,
-                                     AsyncCallback req_callback,
-                                     SocketAsyncResult req_result) {
-                               socket=req_sock;
-                               buffer=req_buffer;
-                               offset=req_offset;
-                               size=req_size;
-                               sockflags=req_sockflags;
-                               endpoint=req_endpoint;
-                               callback=req_callback;
-                               result=req_result;
-                       }
-
-                       private void End() {
-                               ((ManualResetEvent)result.AsyncWaitHandle).Set();
-                               result.IsCompleted=true;
-                               if (callback != null)
-                                       callback(result);
+                       public Worker (SocketAsyncResult ares)
+                       {
+                               this.result = ares;
                        }
-                       
-                       public void Accept() {
-                               lock(result) {
+
+                       public void Accept ()
+                       {
+                               lock (result) {
+                                       Socket acc_socket = null;
                                        try {
-                                               acc_socket=socket.Accept();
+                                               acc_socket = result.Sock.Accept ();
                                        } catch (Exception e) {
-                                               result.SetDelayedException(e);
+                                               result.Complete (e);
+                                               return;
                                        }
-                                       End();
+
+                                       result.Complete (acc_socket);
                                }
                        }
 
-                       public void Connect() {
-                               lock(result) {
-                                       if (socket.Blocking) {
-                                               try {
-                                                       socket.Connect(endpoint);
-                                               } catch (Exception e) {
-                                                       result.SetDelayedException(e);
-                                               }
-                                               End ();
-                                               return;
-                                       }
-
-                                       SocketException rethrow = null;
+                       public void Connect ()
+                       {
+                               lock (result) {
                                        try {
-                                               socket.Connect (endpoint);
-                                       } catch (SocketException e) {
-                                               //WSAEINPROGRESS
-                                               if (e.NativeErrorCode != 10036) {
-                                                       result.SetDelayedException(e);  
-                                                       End ();
+                                               result.Sock.Connect (result.EndPoint);
+                                       } catch (SocketException se) {
+                                               if (result.Sock.blocking || se.ErrorCode != 10036) {
+                                                       result.Complete (se);
                                                        return;
                                                }
-
-                                               socket.Poll (-1, SelectMode.SelectWrite);
+                                               
                                                try {
-                                                       socket.Connect (endpoint);
-                                               } catch (SocketException e2) {
-                                                       rethrow = e2;
+                                                       result.Sock.Poll (-1, SelectMode.SelectWrite);
+                                                       result.Sock.Connect (result.EndPoint);
+                                               } catch (Exception k) {
+                                                       result.Complete (k);
+                                                       return;
                                                }
+                                       } catch (Exception e) {
+                                               result.Complete (e);
+                                               return;
                                        }
-                                       if (rethrow != null)
-                                               result.SetDelayedException(rethrow);
-                                       End ();
+
+                                       result.Complete ();
                                }
                        }
 
-                       public void Receive() {
-                               lock(result) {
-                                       if (socket.Blocking) {
-                                               try {
-                                                       total=socket.Receive(buffer, offset,
-                                                                            size, sockflags);
-                                               } catch (Exception e) {
-                                                       result.SetDelayedException(e);
-                                               }
-                                               End();
-                                               return;
-                                       }
-
-                                       SocketException rethrow = null;
+                       public void Receive ()
+                       {
+                               lock (result) {
+                                       int total = 0;
                                        try {
-                                               total = socket.Receive (buffer, offset, size, sockflags);
-                                       } catch (SocketException e) {
-                                               //WSAEWOULDBLOCK
-                                               if (e.NativeErrorCode != 10035) {
-                                                       result.SetDelayedException(e);
-                                                       End ();
-                                                        return;
-                                               }
+                                               if (!result.Sock.blocking)
+                                                       result.Sock.Poll (-1, SelectMode.SelectRead);
 
-                                               socket.Poll (-1, SelectMode.SelectRead);
-                                               try {
-                                                       total = socket.Receive (buffer, offset, size, sockflags);
-                                               } catch (SocketException e2) {
-                                                       rethrow = e2;
-                                               }
+                                               total = result.Sock.Receive_nochecks (result.Buffer,
+                                                                            result.Offset,
+                                                                            result.Size,
+                                                                            result.SockFlags);
+                                       } catch (Exception e) {
+                                               result.Complete (e);
+                                               return;
                                        }
-                                       if (rethrow != null)
-                                               result.SetDelayedException(rethrow);
-                                       End ();
+
+                                       result.Complete (total);
                                }
                        }
 
-                       public void ReceiveFrom() {
-                               lock(result) {
+                       public void ReceiveFrom ()
+                       {
+                               lock (result) {
+                                       int total = 0;
                                        try {
-                                               total=socket.ReceiveFrom(buffer,
-                                                                        offset, size,
-                                                                        sockflags,
-                                                                        ref endpoint);
+                                               if (!result.Sock.blocking)
+                                                       result.Sock.Poll (-1, SelectMode.SelectRead);
+
+                                               total = result.Sock.ReceiveFrom_nochecks (result.Buffer,
+                                                                                result.Offset,
+                                                                                result.Size,
+                                                                                result.SockFlags,
+                                                                                ref result.EndPoint);
                                        } catch (Exception e) {
-                                                result.SetDelayedException(e);
+                                               result.Complete (e);
+                                               return;
                                        }
-                                       End();
+
+                                       result.Complete (total);
                                }
                        }
 
-                       public void Send() {
-                               lock(result) {
+                       public void Send ()
+                       {
+                               lock (result) {
+                                       int total = 0;
                                        try {
-                                               total=socket.Send(buffer, offset, size,
-                                                                 sockflags);
+                                               if (!result.Sock.blocking)
+                                                       result.Sock.Poll (-1, SelectMode.SelectWrite);
+
+                                               total = result.Sock.Send_nochecks (result.Buffer,
+                                                                         result.Offset,
+                                                                         result.Size,
+                                                                         result.SockFlags);
                                        } catch (Exception e) {
-                                               result.SetDelayedException(e);
+                                               result.Complete (e);
+                                               return;
                                        }
-                                       End();
+
+                                       result.Complete (total);
                                }
                        }
 
                        public void SendTo() {
-                               lock(result) {
+                               lock (result) {
+                                       int total = 0;
                                        try {
-                                               total=socket.SendTo(buffer, offset,
-                                                                   size, sockflags,
-                                                                   endpoint);
+                                               if (!result.Sock.blocking)
+                                                       result.Sock.Poll (-1, SelectMode.SelectWrite);
+
+                                               total = result.Sock.SendTo_nochecks (result.Buffer,
+                                                                           result.Offset,
+                                                                           result.Size,
+                                                                           result.SockFlags,
+                                                                           result.EndPoint);
                                        } catch (Exception e) {
-                                                result.SetDelayedException(e);
-                                        }
-                                       End();
-                               }
-                       }
-
-                       public EndPoint EndPoint {
-                               get {
-                                       return(endpoint);
-                               }
-                       }
-
-                       public Socket Socket {
-                               get {
-                                       return(acc_socket);
-                               }
-                       }
+                                               result.Complete (e);
+                                               return;
+                                       }
 
-                       public int Total {
-                               get {
-                                       return(total);
+                                       result.Complete (total);
                                }
                        }
                }
@@ -297,13 +300,15 @@ namespace System.Net.Sockets
                private AddressFamily address_family;
                private SocketType socket_type;
                private ProtocolType protocol_type;
-               private bool blocking=true;
+               internal bool blocking=true;
                private int pendingEnds;
                private int closeDelayed;
-\r
-               /*\r
-                *      These two fields are looked up by name by the runtime, don't change\r
-                *  their name without also updating the runtime code.\r
+               static readonly bool supportsAsync = FakeGetSupportsAsync ();
+
+               delegate void SocketAsyncCall ();
+               /*
+                *      These two fields are looked up by name by the runtime, don't change
+                *  their name without also updating the runtime code.
                 */
                private static int ipv4Supported = -1, ipv6Supported = -1;
 
@@ -324,7 +329,8 @@ namespace System.Net.Sockets
                private extern static void Select_internal(ref Socket[] read,
                                                           ref Socket[] write,
                                                           ref Socket[] err,
-                                                          int timeout);
+                                                          int timeout,
+                                                          out int error);
 
                public static void Select(IList read_list, IList write_list,
                                          IList err_list, int time_us) {
@@ -384,8 +390,14 @@ namespace System.Net.Sockets
                                }
                        }
 
+                       int error;
+                       
                        Select_internal(ref read_arr, ref write_arr,
-                                       ref err_arr, time_us);
+                                       ref err_arr, time_us, out error);
+
+                       if(error != 0) {
+                               throw new SocketException (error);
+                       }
 
                        if(read_list!=null) {
                                read_list.Clear();
@@ -413,7 +425,7 @@ namespace System.Net.Sockets
                        Assembly ass;
                        
                        try {
-                               ass=Assembly.Load("Mono.Posix");
+                               ass = Assembly.Load (Consts.AssemblyMono_Posix);
                        } catch (FileNotFoundException) {
                                return;
                        }
@@ -449,7 +461,8 @@ namespace System.Net.Sockets
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern IntPtr Socket_internal(AddressFamily family,
                                                      SocketType type,
-                                                     ProtocolType proto);
+                                                     ProtocolType proto,
+                                                     out int error);
                
                public Socket(AddressFamily family, SocketType type,
                              ProtocolType proto) {
@@ -457,7 +470,12 @@ namespace System.Net.Sockets
                        socket_type=type;
                        protocol_type=proto;
                        
-                       socket=Socket_internal(family, type, proto);
+                       int error;
+                       
+                       socket=Socket_internal(family, type, proto, out error);
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
                public AddressFamily AddressFamily {
@@ -468,27 +486,44 @@ namespace System.Net.Sockets
 
                // Returns the amount of data waiting to be read on socket
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static int Available_internal(IntPtr socket);
+               private extern static int Available_internal(IntPtr socket,
+                                                            out int error);
                
                public int Available {
                        get {
                                if (disposed && closed)
                                        throw new ObjectDisposedException (GetType ().ToString ());
 
-                               return(Available_internal(socket));
+                               int ret, error;
+                               
+                               ret = Available_internal(socket, out error);
+
+                               if (error != 0) {
+                                       throw new SocketException (error);
+                               }
+
+                               return(ret);
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void Blocking_internal(IntPtr socket,
-                                                           bool block);
+                                                            bool block,
+                                                            out int error);
 
                public bool Blocking {
                        get {
                                return(blocking);
                        }
                        set {
-                               Blocking_internal(socket, value);
+                               int error;
+                               
+                               Blocking_internal(socket, value, out error);
+
+                               if (error != 0) {
+                                       throw new SocketException (error);
+                               }
+                               
                                blocking=value;
                        }
                }
@@ -507,7 +542,7 @@ namespace System.Net.Sockets
 
                // Returns the local endpoint details in addr and port
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static SocketAddress LocalEndPoint_internal(IntPtr socket);
+               private extern static SocketAddress LocalEndPoint_internal(IntPtr socket, out int error);
 
                [MonoTODO("Support non-IP endpoints")]
                public EndPoint LocalEndPoint {
@@ -516,8 +551,13 @@ namespace System.Net.Sockets
                                        throw new ObjectDisposedException (GetType ().ToString ());
 
                                SocketAddress sa;
+                               int error;
                                
-                               sa=LocalEndPoint_internal(socket);
+                               sa=LocalEndPoint_internal(socket, out error);
+
+                               if (error != 0) {
+                                       throw new SocketException (error);
+                               }
 
                                if(sa.Family==AddressFamily.InterNetwork || sa.Family==AddressFamily.InterNetworkV6) {
                                        // Stupidly, EndPoint.Create() is an
@@ -540,7 +580,7 @@ namespace System.Net.Sockets
 
                // Returns the remote endpoint details in addr and port
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static SocketAddress RemoteEndPoint_internal(IntPtr socket);
+               private extern static SocketAddress RemoteEndPoint_internal(IntPtr socket, out int error);
 
                [MonoTODO("Support non-IP endpoints")]
                public EndPoint RemoteEndPoint {
@@ -549,8 +589,13 @@ namespace System.Net.Sockets
                                        throw new ObjectDisposedException (GetType ().ToString ());
 
                                SocketAddress sa;
+                               int error;
                                
-                               sa=RemoteEndPoint_internal(socket);
+                               sa=RemoteEndPoint_internal(socket, out error);
+
+                               if (error != 0) {
+                                       throw new SocketException (error);
+                               }
 
                                if(sa.Family==AddressFamily.InterNetwork || sa.Family==AddressFamily.InterNetworkV6 ) {
                                        // Stupidly, EndPoint.Create() is an
@@ -572,78 +617,84 @@ namespace System.Net.Sockets
                }
 
 #if NET_1_1
-               public static bool SupportsIPv4 {\r
-                       get {\r
-                               CheckProtocolSupport();\r
-                               return ipv4Supported == 1;\r
-                       }\r
+               public static bool SupportsIPv4 {
+                       get {
+                               CheckProtocolSupport();
+                               return ipv4Supported == 1;
+                       }
                }
 
-               public static bool SupportsIPv6 {\r
-                       get {\r
-                               CheckProtocolSupport();\r
-                               return ipv6Supported == 1;\r
-                       }\r
+               public static bool SupportsIPv6 {
+                       get {
+                               CheckProtocolSupport();
+                               return ipv6Supported == 1;
+                       }
                }
 #else
-               internal static bool SupportsIPv4 \r
-               {\r
-                       get \r
-                       {\r
-                               return true;\r
-                       }\r
-               }
-
-               internal static bool SupportsIPv6 \r
-               {\r
-                       get \r
-                       {\r
-                               return false;\r
-                       }\r
-               }\r
+               internal static bool SupportsIPv4 
+               {
+                       get 
+                       {
+                               return true;
+                       }
+               }
+
+               internal static bool SupportsIPv6 
+               {
+                       get 
+                       {
+                               return false;
+                       }
+               }
 #endif
 
                internal static void CheckProtocolSupport()
-               {\r
-                       if(ipv4Supported == -1) {\r
-                               try  {\r
-                                       Socket tmp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);\r
-                                       tmp.Close();\r
-\r
-                                       ipv4Supported = 1;\r
-                               }\r
-                               catch {\r
-                                       ipv4Supported = 0;\r
-                               }\r
-                       }\r
-\r
-                       if(ipv6Supported == -1) {\r
-                               NetConfig config = (NetConfig)System.Configuration.ConfigurationSettings.GetConfig("system.net/settings");\r
-\r
-                               if(config != null)\r
-                                       ipv6Supported = config.ipv6Enabled?-1:0;\r
-\r
-                               if(ipv6Supported != 0) {\r
-                                       try {\r
-                                               Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);\r
-                                               tmp.Close();\r
-\r
-                                               ipv6Supported = 1;\r
-                                       }\r
-                                       catch { }\r
-                               }\r
-                       }\r
+               {
+                       if(ipv4Supported == -1) {
+                               try  {
+                                       Socket tmp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                                       tmp.Close();
+
+                                       ipv4Supported = 1;
+                               }
+                               catch {
+                                       ipv4Supported = 0;
+                               }
+                       }
+
+                       if(ipv6Supported == -1) {
+                               NetConfig config = (NetConfig)System.Configuration.ConfigurationSettings.GetConfig("system.net/settings");
+
+                               if(config != null)
+                                       ipv6Supported = config.ipv6Enabled?-1:0;
+
+                               if(ipv6Supported != 0) {
+                                       try {
+                                               Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
+                                               tmp.Close();
+
+                                               ipv6Supported = 1;
+                                       }
+                                       catch { }
+                               }
+                       }
                }
 
                // Creates a new system socket, returning the handle
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static IntPtr Accept_internal(IntPtr sock);
+               private extern static IntPtr Accept_internal(IntPtr sock,
+                                                            out int error);
                
                public Socket Accept() {
                        if (disposed && closed)
                                throw new ObjectDisposedException (GetType ().ToString ());
 
-                       IntPtr sock=Accept_internal(socket);
+                       int error;
+                       IntPtr sock=Accept_internal(socket, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                        
                        return(new Socket(this.AddressFamily, this.SocketType,
                                          this.ProtocolType, sock));
@@ -656,12 +707,10 @@ namespace System.Net.Sockets
                                throw new ObjectDisposedException (GetType ().ToString ());
 
                        Interlocked.Increment (ref pendingEnds);
-                       SocketAsyncResult req=new SocketAsyncResult(state);
-                       Worker worker=new Worker(this, callback, req);
-                       req.Worker=worker;
-                       Thread child=new Thread(new ThreadStart(worker.Accept));
-                       child.IsBackground = true;
-                       child.Start();
+                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback);
+                       Worker worker = new Worker (req);
+                       SocketAsyncCall sac = new SocketAsyncCall (worker.Accept);
+                       sac.BeginInvoke (null, null);
                        return(req);
                }
 
@@ -676,13 +725,11 @@ namespace System.Net.Sockets
                                throw new ArgumentNullException ("end_point");
 
                        Interlocked.Increment (ref pendingEnds);
-                       SocketAsyncResult req=new SocketAsyncResult(state);
-                       Worker worker=new Worker(this, end_point, callback,
-                                                req);
-                       req.Worker=worker;
-                       Thread child=new Thread(new ThreadStart(worker.Connect));
-                       child.IsBackground = true;
-                       child.Start();
+                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback);
+                       req.EndPoint = end_point;
+                       Worker worker = new Worker (req);
+                       SocketAsyncCall sac = new SocketAsyncCall (worker.Connect);
+                       sac.BeginInvoke (null, null);
                        return(req);
                }
 
@@ -698,24 +745,32 @@ namespace System.Net.Sockets
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
-                       if (offset < 0)
-                               throw new ArgumentOutOfRangeException ("offset must be >= 0");
-
-                       if (size < 0)
-                               throw new ArgumentOutOfRangeException ("size must be >= 0");
+                       if (offset < 0 || offset > buffer.Length)
+                               throw new ArgumentOutOfRangeException ("offset");
 
-                       if (offset + size > buffer.Length)
-                               throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
+                       if (size < 0 || offset + size > buffer.Length)
+                               throw new ArgumentOutOfRangeException ("size");
 
                        Interlocked.Increment (ref pendingEnds);
-                       SocketAsyncResult req=new SocketAsyncResult(state);
-                       Worker worker=new Worker(this, buffer, offset, size,
-                                                socket_flags, callback, req);
-                       req.Worker=worker;
-                       Thread child=new Thread(new ThreadStart(worker.Receive));
-                       child.IsBackground = true;
-                       child.Start();
-                       return(req);
+                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback);
+                       req.Buffer = buffer;
+                       req.Offset = offset;
+                       req.Size = size;
+                       req.SockFlags = socket_flags;
+                       if (supportsAsync && socket_type == SocketType.Stream) {
+                               int error;
+                               req.CreateAsyncDelegate ();
+                               KeepReference (req);
+                               AsyncReceiveInternal (req, out error);
+                               if (error != 10036) // WSAEINPROGRESS
+                                       throw new SocketException (error);
+                       } else {
+                               Worker worker = new Worker (req);
+                               SocketAsyncCall sac = new SocketAsyncCall (worker.Receive);
+                               sac.BeginInvoke (null, null);
+                       }
+
+                       return req;
                }
 
                public IAsyncResult BeginReceiveFrom(byte[] buffer, int offset,
@@ -740,22 +795,21 @@ namespace System.Net.Sockets
                                throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
 
                        Interlocked.Increment (ref pendingEnds);
-                       SocketAsyncResult req=new SocketAsyncResult(state);
-                       Worker worker=new Worker(this, buffer, offset, size,
-                                                socket_flags, remote_end,
-                                                callback, req);
-                       req.Worker=worker;
-                       Thread child=new Thread(new ThreadStart(worker.ReceiveFrom));
-                       child.IsBackground = true;
-                       child.Start();
-                       return(req);
-               }
-
-               public IAsyncResult BeginSend(byte[] buffer, int offset,
-                                             int size,
-                                             SocketFlags socket_flags,
-                                             AsyncCallback callback,
-                                             object state) {
+                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback);
+                       req.Buffer = buffer;
+                       req.Offset = offset;
+                       req.Size = size;
+                       req.SockFlags = socket_flags;
+                       req.EndPoint = remote_end;
+                       Worker worker = new Worker (req);
+                       SocketAsyncCall sac = new SocketAsyncCall (worker.ReceiveFrom);
+                       sac.BeginInvoke (null, null);
+                       return req;
+               }
+
+               public IAsyncResult BeginSend (byte[] buffer, int offset, int size, SocketFlags socket_flags,
+                                              AsyncCallback callback, object state)
+               {
                        if (disposed && closed)
                                throw new ObjectDisposedException (GetType ().ToString ());
 
@@ -772,14 +826,24 @@ namespace System.Net.Sockets
                                throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
 
                        Interlocked.Increment (ref pendingEnds);
-                       SocketAsyncResult req=new SocketAsyncResult(state);
-                       Worker worker=new Worker(this, buffer, offset, size,
-                                                socket_flags, callback, req);
-                       req.Worker=worker;
-                       Thread child=new Thread(new ThreadStart(worker.Send));
-                       child.IsBackground = true;
-                       child.Start();
-                       return(req);
+                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback);
+                       req.Buffer = buffer;
+                       req.Offset = offset;
+                       req.Size = size;
+                       req.SockFlags = socket_flags;
+                       if (supportsAsync && socket_type == SocketType.Stream) {
+                               int error;
+                               req.CreateAsyncDelegate ();
+                               KeepReference (req);
+                               AsyncSendInternal (req, out error);
+                               if (error != 10036) // WSAEINPROGRESS
+                                       throw new SocketException (error);
+                       } else {
+                               Worker worker = new Worker (req);
+                               SocketAsyncCall sac = new SocketAsyncCall (worker.Send);
+                               sac.BeginInvoke (null, null);
+                       }
+                       return req;
                }
 
                public IAsyncResult BeginSendTo(byte[] buffer, int offset,
@@ -804,21 +868,23 @@ namespace System.Net.Sockets
                                throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
 
                        Interlocked.Increment (ref pendingEnds);
-                       SocketAsyncResult req=new SocketAsyncResult(state);
-                       Worker worker=new Worker(this, buffer, offset, size,
-                                                socket_flags, remote_end,
-                                                callback, req);
-                       req.Worker=worker;
-                       Thread child=new Thread(new ThreadStart(worker.SendTo));
-                       child.IsBackground = true;
-                       child.Start();
-                       return(req);
+                       SocketAsyncResult req = new SocketAsyncResult (this, state, callback);
+                       req.Buffer = buffer;
+                       req.Offset = offset;
+                       req.Size = size;
+                       req.SockFlags = socket_flags;
+                       req.EndPoint = remote_end;
+                       Worker worker = new Worker(req);
+                       SocketAsyncCall sac = new SocketAsyncCall (worker.SendTo);
+                       sac.BeginInvoke (null, null);
+                       return req;
                }
 
                // Creates a new system socket, returning the handle
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void Bind_internal(IntPtr sock,
-                                                        SocketAddress sa);
+                                                        SocketAddress sa,
+                                                        out int error);
 
                public void Bind(EndPoint local_end) {
                        if (disposed && closed)
@@ -828,12 +894,20 @@ namespace System.Net.Sockets
                                throw new ArgumentNullException("local_end");
                        }
                        
-                       Bind_internal(socket, local_end.Serialize());
+                       int error;
+                       
+                       Bind_internal(socket, local_end.Serialize(),
+                                     out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
                // Closes the socket
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void Close_internal(IntPtr socket);
+               private extern static void Close_internal(IntPtr socket,
+                                                         out int error);
                
                public void Close() {
                        ((IDisposable) this).Dispose ();
@@ -842,7 +916,8 @@ namespace System.Net.Sockets
                // Connects to the remote address
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void Connect_internal(IntPtr sock,
-                                                           SocketAddress sa);
+                                                           SocketAddress sa,
+                                                           out int error);
 
                public void Connect(EndPoint remote_end) {
                        if (disposed && closed)
@@ -852,7 +927,18 @@ namespace System.Net.Sockets
                                throw new ArgumentNullException("remote_end");
                        }
 
-                       Connect_internal(socket, remote_end.Serialize());
+                       int error;
+                       
+                       SocketAddress serial = remote_end.Serialize ();
+                       Connect_internal(socket, serial, out error);
+                       if (!blocking && error == 10036) {
+                               Poll (-1, SelectMode.SelectWrite);
+                               Connect_internal (socket, serial, out error);
+                       }
+
+                       if (error != 0)
+                               throw new SocketException (error);
+                       
                        connected=true;
                }
                
@@ -865,13 +951,15 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = result as SocketAsyncResult;
                        if (req == null)
-                               throw new ArgumentException ("Invalid IAsyncResult");
+                               throw new ArgumentException ("Invalid IAsyncResult", "result");
+
+                       if (!result.IsCompleted)
+                               result.AsyncWaitHandle.WaitOne();
 
-                       result.AsyncWaitHandle.WaitOne();
                        Interlocked.Decrement (ref pendingEnds);
                        CheckIfClose ();
                        req.CheckIfThrowDelayedException();
-                       return(req.Worker.Socket);
+                       return req.Socket;
                }
 
                public void EndConnect(IAsyncResult result) {
@@ -883,9 +971,11 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = result as SocketAsyncResult;
                        if (req == null)
-                               throw new ArgumentException ("Invalid IAsyncResult");
+                               throw new ArgumentException ("Invalid IAsyncResult", "result");
+
+                       if (!result.IsCompleted)
+                               result.AsyncWaitHandle.WaitOne();
 
-                       result.AsyncWaitHandle.WaitOne();
                        Interlocked.Decrement (ref pendingEnds);
                        CheckIfClose ();
                        req.CheckIfThrowDelayedException();
@@ -900,13 +990,16 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = result as SocketAsyncResult;
                        if (req == null)
-                               throw new ArgumentException ("Invalid IAsyncResult");
+                               throw new ArgumentException ("Invalid IAsyncResult", "result");
+
+                       RemoveReference (req);
+                       if (!result.IsCompleted)
+                               result.AsyncWaitHandle.WaitOne();
 
-                       result.AsyncWaitHandle.WaitOne();
                        Interlocked.Decrement (ref pendingEnds);
                        CheckIfClose ();
                        req.CheckIfThrowDelayedException();
-                       return(req.Worker.Total);
+                       return req.Total;
                }
 
                public int EndReceiveFrom(IAsyncResult result,
@@ -919,14 +1012,16 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = result as SocketAsyncResult;
                        if (req == null)
-                               throw new ArgumentException ("Invalid IAsyncResult");
+                               throw new ArgumentException ("Invalid IAsyncResult", "result");
+
+                       if (!result.IsCompleted)
+                               result.AsyncWaitHandle.WaitOne();
 
-                       result.AsyncWaitHandle.WaitOne();
                        Interlocked.Decrement (ref pendingEnds);
                        CheckIfClose ();
                        req.CheckIfThrowDelayedException();
-                       end_point=req.Worker.EndPoint;
-                       return(req.Worker.Total);
+                       end_point = req.EndPoint;
+                       return req.Total;
                }
 
                public int EndSend(IAsyncResult result) {
@@ -938,13 +1033,16 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = result as SocketAsyncResult;
                        if (req == null)
-                               throw new ArgumentException ("Invalid IAsyncResult");
+                               throw new ArgumentException ("Invalid IAsyncResult", "result");
+
+                       RemoveReference (req);
+                       if (!result.IsCompleted)
+                               result.AsyncWaitHandle.WaitOne();
 
-                       result.AsyncWaitHandle.WaitOne();
                        Interlocked.Decrement (ref pendingEnds);
                        CheckIfClose ();
                        req.CheckIfThrowDelayedException();
-                       return(req.Worker.Total);
+                       return req.Total;
                }
 
                public int EndSendTo(IAsyncResult result) {
@@ -956,34 +1054,49 @@ namespace System.Net.Sockets
 
                        SocketAsyncResult req = result as SocketAsyncResult;
                        if (req == null)
-                               throw new ArgumentException ("Invalid IAsyncResult");
+                               throw new ArgumentException ("Invalid IAsyncResult", "result");
+
+                       if (!result.IsCompleted)
+                               result.AsyncWaitHandle.WaitOne();
 
-                       result.AsyncWaitHandle.WaitOne();
                        Interlocked.Decrement (ref pendingEnds);
                        CheckIfClose ();
                        req.CheckIfThrowDelayedException();
-                       return(req.Worker.Total);
+                       return req.Total;
                }
 
                void CheckIfClose ()
                {
-                       if (Interlocked.CompareExchange (ref closeDelayed, 1, 0) == 1) {
+                       if (Interlocked.CompareExchange (ref closeDelayed, 0, 1) == 1 &&
+                           Interlocked.CompareExchange (ref pendingEnds, 0, 0) == 0) {
                                closed = true;
-                               Close_internal(socket);
+
+                               int error;
+                               
+                               Close_internal(socket, out error);
+
+                               if (error != 0) {
+                                       throw new SocketException (error);
+                               }
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val);
+               private extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error);
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val);
+               private extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error);
 
                public object GetSocketOption(SocketOptionLevel level,
                                              SocketOptionName name) {
                        object obj_val;
+                       int error;
                        
                        GetSocketOption_obj_internal(socket, level, name,
-                                                    out obj_val);
+                                                    out obj_val, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                        
                        if(name==SocketOptionName.Linger) {
                                return((LingerOption)obj_val);
@@ -999,70 +1112,118 @@ namespace System.Net.Sockets
                                            SocketOptionName name,
                                            byte[] opt_value) {
                        int opt_value_len=opt_value.Length;
+                       int error;
                        
                        GetSocketOption_arr_internal(socket, level, name,
-                                                    ref opt_value);
+                                                    ref opt_value, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
                public byte[] GetSocketOption(SocketOptionLevel level,
                                              SocketOptionName name,
                                              int length) {
                        byte[] byte_val=new byte[length];
+                       int error;
                        
                        GetSocketOption_arr_internal(socket, level, name,
-                                                    ref byte_val);
+                                                    ref byte_val, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
 
                        return(byte_val);
                }
 
-               [MonoTODO("Totally undocumented")]
-               public int IOControl(int ioctl_code, byte[] in_value,
-                                    byte[] out_value) {
-                       throw new NotImplementedException();
+               // See Socket.IOControl, WSAIoctl documentation in MSDN. The
+               // common options between UNIX and Winsock are FIONREAD,
+               // FIONBIO and SIOCATMARK. Anything else will depend on the
+               // system.
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static int WSAIoctl (IntPtr sock, int ioctl_code,
+                                           byte [] input, byte [] output,
+                                           out int error);
+
+               public int IOControl (int ioctl_code, byte [] in_value, byte [] out_value)
+               {
+                       if (disposed)
+                               throw new ObjectDisposedException (GetType ().ToString ());
+
+                       int error;
+                       int result = WSAIoctl (socket, ioctl_code, in_value,
+                                              out_value, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
+                       
+                       if (result == -1)
+                               throw new InvalidOperationException ("Must use Blocking property instead.");
+
+                       return result;
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static void Listen_internal(IntPtr sock,
-                                                          int backlog);
+                                                          int backlog,
+                                                          out int error);
 
                public void Listen(int backlog) {
-                       Listen_internal(socket, backlog);
+                       int error;
+                       
+                       Listen_internal(socket, backlog, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
-               /* The docs for Poll() are a bit lightweight too, but
-                * it seems to be just a simple wrapper around Select.
-                */
-               public bool Poll(int time_us, SelectMode mode) {
-                       Socket [] socketlist = new Socket []{this};
-                       Socket [] n = null;
-
-                       switch(mode) {
-                       case SelectMode.SelectError:
-                               Select_internal (ref n, ref n, ref socketlist, time_us);
-                               break;
-                       case SelectMode.SelectRead:
-                               Select_internal (ref socketlist, ref n, ref n, time_us);
-                               break;
-                       case SelectMode.SelectWrite:
-                               Select_internal (ref n, ref socketlist, ref n, time_us);
-                               break;
-                       default:
-                               throw new NotSupportedException();
-                       }
-
-                       return (socketlist.Length == 1);
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error);
+
+               public bool Poll (int time_us, SelectMode mode)
+               {
+                       if (mode != SelectMode.SelectRead &&
+                           mode != SelectMode.SelectWrite &&
+                           mode != SelectMode.SelectError)
+                               throw new NotSupportedException ("'mode' parameter is not valid.");
+
+                       int error;
+                       bool result = Poll_internal (socket, mode, time_us, out error);
+                       if (error != 0)
+                               throw new SocketException (error);
+
+                       return result;
                }
                
-               public int Receive(byte[] buf) {
-                       return(Receive(buf, 0, buf.Length, SocketFlags.None));
+               public int Receive (byte [] buf)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       return Receive_nochecks (buf, 0, buf.Length, SocketFlags.None);
                }
 
-               public int Receive(byte[] buf, SocketFlags flags) {
-                       return(Receive(buf, 0, buf.Length, flags));
+               public int Receive (byte [] buf, SocketFlags flags)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       return Receive_nochecks (buf, 0, buf.Length, flags);
                }
 
-               public int Receive(byte[] buf, int size, SocketFlags flags) {
-                       return(Receive(buf, 0, size, flags));
+               public int Receive (byte [] buf, int size, SocketFlags flags)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       if (size < 0 || size > buf.Length)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       return Receive_nochecks (buf, 0, size, flags);
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -1070,49 +1231,75 @@ namespace System.Net.Sockets
                                                           byte[] buffer,
                                                           int offset,
                                                           int count,
-                                                          SocketFlags flags);
+                                                          SocketFlags flags,
+                                                          out int error);
 
-               public int Receive(byte[] buf, int offset, int size,
-                                  SocketFlags flags) {
-                       if(buf==null) {
-                               throw new ArgumentNullException("buffer is null");
-                       }
-                       if(offset<0 || offset > buf.Length) {
-                               throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
-                       }
-                       if(size<0 || offset+size > buf.Length) {
-                               throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
-                       }
+               public int Receive (byte [] buf, int offset, int size, SocketFlags flags)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       if (offset < 0 || offset > buf.Length)
+                               throw new ArgumentOutOfRangeException ("offset");
+
+                       if (size < 0 || offset + size > buf.Length)
+                               throw new ArgumentOutOfRangeException ("size");
                        
-                       int ret;
+                       return Receive_nochecks (buf, offset, size, flags);
+               }
+
+               int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags)
+               {
+                       int ret, error;
                        
-                       try {
-                               ret=Receive_internal(socket, buf, offset,
-                                                    size, flags);
-                       } catch(SocketException) {
-                               connected=false;
-                               throw;
+                       ret = Receive_internal (socket, buf, offset, size, flags, out error);
+
+                       if (error != 0) {
+                               connected = false;
+                               throw new SocketException (error);
                        }
-                       connected=true;
+                       
+                       connected = true;
 
-                       return(ret);
+                       return ret;
                }
                
-               public int ReceiveFrom(byte[] buf, ref EndPoint remote_end) {
-                       return(ReceiveFrom(buf, 0, buf.Length,
-                                          SocketFlags.None, ref remote_end));
+               public int ReceiveFrom (byte [] buf, ref EndPoint remote_end)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
+
+                       return ReceiveFrom_nochecks (buf, 0, buf.Length, SocketFlags.None, ref remote_end);
                }
 
-               public int ReceiveFrom(byte[] buf, SocketFlags flags,
-                                      ref EndPoint remote_end) {
-                       return(ReceiveFrom(buf, 0, buf.Length, flags,
-                                          ref remote_end));
+               public int ReceiveFrom (byte [] buf, SocketFlags flags, ref EndPoint remote_end)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
+
+
+                       return ReceiveFrom_nochecks (buf, 0, buf.Length, flags, ref remote_end);
                }
 
-               public int ReceiveFrom(byte[] buf, int size, SocketFlags flags,
-                                      ref EndPoint remote_end) {
-                       return(ReceiveFrom(buf, 0, size, flags,
-                                          ref remote_end));
+               public int ReceiveFrom (byte [] buf, int size, SocketFlags flags,
+                                       ref EndPoint remote_end)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
+
+                       if (size < 0 || size > buf.Length)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       return ReceiveFrom_nochecks (buf, 0, size, flags, ref remote_end);
                }
 
 
@@ -1122,63 +1309,84 @@ namespace System.Net.Sockets
                                                            int offset,
                                                            int count,
                                                            SocketFlags flags,
-                                                           ref SocketAddress sockaddr);
+                                                           ref SocketAddress sockaddr,
+                                                           out int error);
 
-               public int ReceiveFrom(byte[] buf, int offset, int size,
-                                      SocketFlags flags,
-                                      ref EndPoint remote_end) {
-                       if(buf==null) {
-                               throw new ArgumentNullException("buffer is null");
-                       }
-                       if(remote_end==null) {
-                               throw new ArgumentNullException("remote endpoint is null");
-                       }
-                       if(offset<0 || offset>buf.Length) {
-                               throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
-                       }
-                       if(size<0 || offset+size>buf.Length) {
-                               throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
-                       }
+               public int ReceiveFrom (byte [] buf, int offset, int size, SocketFlags flags,
+                                       ref EndPoint remote_end)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
 
-                       SocketAddress sockaddr=remote_end.Serialize();
-                       int count;
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
 
-                       try {
-                               count=RecvFrom_internal(socket, buf, offset,
-                                                       size, flags,
-                                                       ref sockaddr);
-                       } catch(SocketException) {
-                               connected=false;
-                               throw;
+                       if (offset < 0 || offset > buf.Length)
+                               throw new ArgumentOutOfRangeException ("offset");
+
+                       if (size < 0 || offset + size > buf.Length)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       return ReceiveFrom_nochecks (buf, offset, size, flags, ref remote_end);
+               }
+
+               int ReceiveFrom_nochecks (byte [] buf, int offset, int size, SocketFlags flags,
+                                         ref EndPoint remote_end)
+               {
+                       SocketAddress sockaddr = remote_end.Serialize();
+                       int cnt, error;
+
+                       cnt = RecvFrom_internal (socket, buf, offset, size, flags, ref sockaddr, out error);
+
+                       if (error != 0) {
+                               connected = false;
+                               throw new SocketException (error);
                        }
-                       connected=true;
+
+                       connected = true;
                        
                        // Stupidly, EndPoint.Create() is an
                        // instance method
-                       remote_end=remote_end.Create(sockaddr);
+                       remote_end = remote_end.Create (sockaddr);
 
-                       return(count);
+                       return cnt;
                }
 
-               public int Send(byte[] buf) {
-                       return(Send(buf, 0, buf.Length, SocketFlags.None));
+               public int Send (byte [] buf)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       return Send_nochecks (buf, 0, buf.Length, SocketFlags.None);
                }
 
-               public int Send(byte[] buf, SocketFlags flags) {
-                       return(Send(buf, 0, buf.Length, flags));
+               public int Send (byte [] buf, SocketFlags flags)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       return Send_nochecks (buf, 0, buf.Length, flags);
                }
 
-               public int Send(byte[] buf, int size, SocketFlags flags) {
-                       return(Send(buf, 0, size, flags));
+               public int Send (byte [] buf, int size, SocketFlags flags)
+               {
+                       if (buf == null)
+                               throw new ArgumentNullException ("buf");
+
+                       if (size < 0 || size > buf.Length)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       return Send_nochecks (buf, 0, size, flags);
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern static int Send_internal(IntPtr sock,
                                                        byte[] buf, int offset,
                                                        int count,
-                                                       SocketFlags flags);
+                                                       SocketFlags flags,
+                                                       out int error);
 
-               public int Send (byte[] buf, int offset, int size, SocketFlags flags)
+               public int Send (byte [] buf, int offset, int size, SocketFlags flags)
                {
                        if (buf == null)
                                throw new ArgumentNullException ("buffer");
@@ -1189,36 +1397,62 @@ namespace System.Net.Sockets
                        if (size < 0 || offset + size > buf.Length)
                                throw new ArgumentOutOfRangeException ("size");
 
+                       return Send_nochecks (buf, offset, size, flags);
+               }
+
+               int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags)
+               {
                        if (size == 0)
                                return 0;
 
-                       int ret;
+                       int ret, error;
 
-                       try {
-                               ret = Send_internal (socket, buf, offset, size, flags);
-                       } catch (SocketException) {
+                       ret = Send_internal (socket, buf, offset, size, flags, out error);
+
+                       if (error != 0) {
                                connected = false;
-                               throw;
+                               throw new SocketException (error);
                        }
+
                        connected = true;
 
                        return ret;
                }
 
-               public int SendTo(byte[] buffer, EndPoint remote_end) {
-                       return(SendTo(buffer, 0, buffer.Length,
-                                     SocketFlags.None, remote_end));
+               public int SendTo (byte [] buffer, EndPoint remote_end)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
+
+                       return SendTo_nochecks (buffer, 0, buffer.Length, SocketFlags.None, remote_end);
                }
 
-               public int SendTo(byte[] buffer, SocketFlags flags,
-                                 EndPoint remote_end) {
-                       return(SendTo(buffer, 0, buffer.Length, flags,
-                                     remote_end));
+               public int SendTo (byte [] buffer, SocketFlags flags, EndPoint remote_end)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
+                               
+                       return SendTo_nochecks (buffer, 0, buffer.Length, flags, remote_end);
                }
 
-               public int SendTo(byte[] buffer, int size, SocketFlags flags,
-                                 EndPoint remote_end) {
-                       return(SendTo(buffer, 0, size, flags, remote_end));
+               public int SendTo (byte [] buffer, int size, SocketFlags flags, EndPoint remote_end)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+
+                       if (remote_end == null)
+                               throw new ArgumentNullException ("remote_end");
+
+                       if (size < 0 || size > buffer.Length)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       return SendTo_nochecks (buffer, 0, size, flags, remote_end);
                }
 
 
@@ -1228,55 +1462,76 @@ namespace System.Net.Sockets
                                                          int offset,
                                                          int count,
                                                          SocketFlags flags,
-                                                         SocketAddress sa);
+                                                         SocketAddress sa,
+                                                         out int error);
 
-               public int SendTo(byte[] buffer, int offset, int size,
-                                 SocketFlags flags, EndPoint remote_end) {
-                       if(buffer==null) {
-                               throw new ArgumentNullException("buffer is null");
-                       }
-                       if(remote_end==null) {
-                               throw new ArgumentNullException("remote endpoint is null");
-                       }
-                       if(offset<0 || offset>buffer.Length) {
-                               throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
-                       }
-                       if(size<0 || offset+size>buffer.Length) {
-                               throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
-                       }
+               public int SendTo (byte [] buffer, int offset, int size, SocketFlags flags,
+                                  EndPoint remote_end)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
 
-                       SocketAddress sockaddr=remote_end.Serialize();
+                       if (remote_end == null)
+                               throw new ArgumentNullException("remote_end");
 
-                       int ret;
+                       if (offset < 0 || offset > buffer.Length)
+                               throw new ArgumentOutOfRangeException ("offset");
 
-                       try {
-                               ret=SendTo_internal(socket, buffer, offset,
-                                                   size, flags, sockaddr);
-                       }
-                       catch(SocketException) {
-                               connected=false;
-                               throw;
+                       if (size < 0 || offset + size > buffer.Length)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       return SendTo_nochecks (buffer, offset, size, flags, remote_end);
+               }
+
+               int SendTo_nochecks (byte [] buffer, int offset, int size, SocketFlags flags,
+                                  EndPoint remote_end)
+               {
+                       SocketAddress sockaddr = remote_end.Serialize ();
+
+                       int ret, error;
+
+                       ret = SendTo_internal (socket, buffer, offset, size, flags, sockaddr, out error);
+
+                       if (error != 0) {
+                               connected = false;
+                               throw new SocketException (error);
                        }
-                       connected=true;
 
-                       return(ret);
+                       connected = true;
+
+                       return ret;
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void SetSocketOption_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte[] byte_val, int int_val);
+               private extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level,
+                                                                    SocketOptionName name, object obj_val,
+                                                                    byte [] byte_val, int int_val,
+                                                                    out int error);
 
                public void SetSocketOption(SocketOptionLevel level,
                                            SocketOptionName name,
                                            byte[] opt_value) {
+                       int error;
+                       
                        SetSocketOption_internal(socket, level, name, null,
-                                                opt_value, 0);
+                                                opt_value, 0, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
                public void SetSocketOption(SocketOptionLevel level,
                                            SocketOptionName name,
                                            int opt_value) {
+                       int error;
+                       
                        SetSocketOption_internal(socket, level, name, null,
-                                                null, opt_value);
+                                                null, opt_value, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
                public void SetSocketOption(SocketOptionLevel level,
@@ -1286,35 +1541,38 @@ namespace System.Net.Sockets
                                throw new ArgumentNullException();
                        }
                        
+                       int error;
+                       
                        /* Passing a bool as the third parameter to
                         * SetSocketOption causes this overload to be
                         * used when in fact we want to pass the value
                         * to the runtime as an int.
                         */
-                       if(opt_value is System.Boolean) {
-                               bool bool_val=(bool)opt_value;
+                       if (opt_value is System.Boolean) {
+                               bool bool_val = (bool) opt_value;
+                               int int_val = (bool_val) ? 1 : 0;
                                
-                               /* Stupid casting rules :-( */
-                               if(bool_val==true) {
-                                       SetSocketOption_internal(socket, level,
-                                                                name, null,
-                                                                null, 1);
-                               } else {
-                                       SetSocketOption_internal(socket, level,
-                                                                name, null,
-                                                                null, 0);
-                               }
+                               SetSocketOption_internal (socket, level, name, null, null, int_val, out error);
                        } else {
-                               SetSocketOption_internal(socket, level, name,
-                                                        opt_value, null, 0);
+                               SetSocketOption_internal (socket, level, name, opt_value, null, 0, out error);
+                       }
+
+                       if (error != 0) {
+                               throw new SocketException (error);
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               private extern static void Shutdown_internal(IntPtr socket, SocketShutdown how);
+               private extern static void Shutdown_internal(IntPtr socket, SocketShutdown how, out int error);
                
                public void Shutdown(SocketShutdown how) {
-                       Shutdown_internal(socket, how);
+                       int error;
+                       
+                       Shutdown_internal(socket, how, out error);
+
+                       if (error != 0) {
+                               throw new SocketException (error);
+                       }
                }
 
                public override int GetHashCode ()
@@ -1325,24 +1583,31 @@ namespace System.Net.Sockets
                private bool disposed;
                
                protected virtual void Dispose(bool explicitDisposing) {
-                       // Check to see if Dispose has already been called
-                       if(!this.disposed) {
-                               // If this is a call to Dispose,
-                               // dispose all managed resources.
-                               if(explicitDisposing) {
-                                       // Free up stuff here
-                               }
+                       if (!disposed) {
+                               int error;
+                               
+                               disposed = true;
+                               connected = false;
+                               if (!explicitDisposing) {
+                                       closed = true;
+                                       Close_internal (socket, out error);
 
-                               // Release unmanaged resources
-                               this.disposed=true;
+                                       if (error != 0) {
+                                               throw new SocketException (error);
+                                       }
+                                       
+                                       return;
+                               }
                        
-                               connected=false;
-                               Interlocked.CompareExchange (ref closeDelayed, 0, 1);
                                if (Interlocked.CompareExchange (ref pendingEnds, 0, 0) == 0) {
-                                       if (Interlocked.CompareExchange (ref closeDelayed, 1, 0) == 1) {
-                                               closed = true;
-                                               Close_internal(socket);
+                                       closed = true;
+                                       Close_internal (socket, out error);
+
+                                       if (error != 0) {
+                                               throw new SocketException (error);
                                        }
+                               } else {
+                                       Interlocked.CompareExchange (ref closeDelayed, 1, 0);
                                }
                        }
                }
@@ -1356,5 +1621,45 @@ namespace System.Net.Sockets
                ~Socket () {
                        Dispose(false);
                }
+
+               static Hashtable asyncObjects;
+
+               static void KeepReference (object o)
+               {
+                       lock (typeof (Socket)) {
+                               if (asyncObjects == null)
+                                       asyncObjects = new Hashtable ();
+
+                               asyncObjects [o] = o;
+                       }
+               }
+
+               static void RemoveReference (object o)
+               {
+                       lock (typeof (Socket)) {
+                               if (asyncObjects == null)
+                                       return;
+
+                               asyncObjects.Remove (o);
+                       }
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static bool GetSupportsAsync ();
+
+               static bool FakeGetSupportsAsync ()
+               {
+                       if (Environment.GetEnvironmentVariable ("MONO_ENABLE_SOCKET_AIO") != null)
+                               return GetSupportsAsync ();
+                       
+                       return false;
+               }
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static void AsyncReceiveInternal (SocketAsyncResult ares, out int error);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static void AsyncSendInternal (SocketAsyncResult ares, out int error);
        }
 }
+