00001 /******************************************************************************* 00002 00003 @file SocketListener.d 00004 00005 Copyright (c) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 4. Derivative works are permitted, but they must carry this notice 00027 in full and credit the original source. 00028 00029 00030 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00031 00032 00033 @version Initial version, June 2004 00034 @author Kris 00035 00036 00037 *******************************************************************************/ 00038 00039 module mango.io.SocketListener; 00040 00041 private import std.thread; 00042 00043 private import mango.io.Socket; 00044 00045 private import mango.io.model.IBuffer, 00046 mango.io.model.IWriter; 00047 00048 /****************************************************************************** 00049 00050 Abstract class to asynchronously listen for incoming data on a 00051 socket. This can be used with DatagramSocket & MulticastSocket, 00052 and might possibly be useful with a basic SocketConduit also. 00053 Note that DatagramSocket must first be bound to a local network 00054 address via bind(), and MulticastSocket should first be made a 00055 member of a multicast group via its join() method. Note also 00056 that the underlying thread is not started by the constructor; 00057 you should do that manually via the start() method. 00058 00059 ******************************************************************************/ 00060 00061 //version = AltListener; 00062 00063 version (AltListener) 00064 alias Object FOO; 00065 else 00066 alias Thread FOO; 00067 00068 class SocketListener : FOO, IListener 00069 { 00070 private bool quit; 00071 private IBuffer buffer; 00072 private ISocketReader reader; 00073 private int limit = 3; 00074 00075 /********************************************************************** 00076 00077 Construct a listener with the requisite arguments. The 00078 specified buffer is populated via the provided instance 00079 of ISocketReader before being passed to the notify() 00080 method. All arguments are required. 00081 00082 **********************************************************************/ 00083 00084 this (ISocketReader reader, IBuffer buffer) 00085 in { 00086 assert (reader); 00087 assert (buffer); 00088 } 00089 body 00090 { 00091 this.buffer = buffer; 00092 this.reader = reader; 00093 version (AltListener) 00094 { 00095 Thread t = new Thread (&run); 00096 t.start(); 00097 } 00098 } 00099 00100 version (AltListener) 00101 void start(){} 00102 00103 /*********************************************************************** 00104 00105 Notification callback invoked whenever the listener has 00106 anything to report. The buffer will have whatever content 00107 was available from the read() operation 00108 00109 ***********************************************************************/ 00110 00111 abstract void notify (IBuffer buffer); 00112 00113 /*********************************************************************** 00114 00115 Handle error conditions from the listener thread. 00116 00117 ***********************************************************************/ 00118 00119 abstract void exception (char[] msg); 00120 00121 /********************************************************************** 00122 00123 Cancel this listener. The thread will quit only after the 00124 current read() request responds, or is interrrupted. 00125 00126 **********************************************************************/ 00127 00128 void cancel () 00129 { 00130 quit = true; 00131 } 00132 00133 /********************************************************************** 00134 00135 Set the maximum contiguous number of exceptions this 00136 listener will survive. Setting a limit of zero will 00137 not survive any errors at all, whereas a limit of two 00138 will survive as long as two consecutive errors don't 00139 arrive back to back. 00140 00141 **********************************************************************/ 00142 00143 void setErrorLimit (ushort limit) 00144 { 00145 this.limit = limit + 1; 00146 } 00147 00148 /********************************************************************** 00149 00150 Execution of this thread is typically stalled on the 00151 read() method belonging to the ISocketReader specified 00152 during construction. You can invoke cancel() to indicate 00153 execution should not proceed further, but that will not 00154 actually interrupt a blocked read() operation. 00155 00156 Note that exceptions are all directed towards the handler 00157 implemented by the class instance. 00158 00159 **********************************************************************/ 00160 00161 int run () 00162 { 00163 int lives = limit; 00164 00165 while (lives > 0) 00166 try { 00167 // start with a clean slate 00168 buffer.clear (); 00169 00170 // wait for incoming content 00171 reader.read (buffer); 00172 00173 // time to quit? Note that a v0.95 compiler bug 00174 // prohibits 'break' from exiting the try{} block 00175 if (quit || Socket.isCancelled ()) 00176 lives = 0; 00177 else 00178 { 00179 // invoke callback 00180 notify (buffer); 00181 lives = limit; 00182 } 00183 } catch (Object x) 00184 // time to quit? 00185 if (quit || Socket.isCancelled ()) 00186 break; 00187 else 00188 { 00189 exception (x.toString); 00190 if (--lives == 0) 00191 exception ("listener thread aborting"); 00192 } 00193 version (TraceLinux) 00194 { 00195 printf ("SocketListener exiting\n"); 00196 } 00197 return 0; 00198 } 00199 } 00200 00201 00202