1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        May 2005: Initial release
8 
9         author:         Kris
10 
11 *******************************************************************************/
12 
13 module tango.io.device.Device;
14 
15 private import  tango.sys.Common;
16 
17 public  import  tango.io.device.Conduit;
18 
19 /*******************************************************************************
20 
21         Implements a means of reading and writing a file device. Conduits
22         are the primary means of accessing external data, and this one is
23         used as a superclass for the console, for files, sockets etc.
24 
25 *******************************************************************************/
26 
27 class Device : Conduit, ISelectable
28 {
29         /// expose superclass definition also
30         public alias Conduit.error error;
31 
32         /***********************************************************************
33 
34                 Throw an IOException noting the last error.
35 
36         ***********************************************************************/
37 
38         final void error ()
39         {
40                 error (this.toString() ~ " :: " ~ SysError.lastMsg);
41         }
42 
43         /***********************************************************************
44 
45                 Return the name of this device.
46 
47         ***********************************************************************/
48 
49         override immutable(char)[] toString ()
50         {
51                 return "<device>";
52         }
53 
54         /***********************************************************************
55 
56                 Return a preferred size for buffering conduit I/O.
57 
58         ***********************************************************************/
59 
60         override const size_t bufferSize ()
61         {
62                 return 1024 * 16;
63         }
64 
65         /***********************************************************************
66 
67                 Windows-specific code.
68 
69         ***********************************************************************/
70 
71         version (Win32)
72         {
73                 struct IO
74                 {
75                         OVERLAPPED      asynch; // must be the first attribute!!
76                         Handle          handle;
77                         bool            track;
78                         void*           task;
79                 }
80 
81                 protected IO io;
82 
83                 /***************************************************************
84 
85                         Allow adjustment of standard IO handles.
86 
87                 ***************************************************************/
88 
89                 protected void reopen (Handle handle)
90                 {
91                         io.handle = handle;
92                 }
93 
94                 /***************************************************************
95 
96                         Return the underlying OS handle of this Conduit.
97 
98                 ***************************************************************/
99 
100                 @property final Handle fileHandle ()
101                 {
102                         return io.handle;
103                 }
104 
105                 /***************************************************************
106 
107                 ***************************************************************/
108 
109                 version(TangoRuntime)
110                 {
111                    override void dispose ()
112                    {
113                            if (io.handle != INVALID_HANDLE_VALUE)
114                                if (scheduler)
115                                    scheduler.close (io.handle, toString);
116                            detach();
117                    }
118                 }
119 
120                 /***************************************************************
121 
122                         Release the underlying file. Note that an exception
123                         is not thrown on error, as doing so can induce some
124                         spaggetti into error handling. Instead, we need to
125                         change this to return a bool instead, so the caller
126                         can decide what to do.
127 
128                 ***************************************************************/
129 
130                 override void detach ()
131                 {
132                         if (io.handle != INVALID_HANDLE_VALUE)
133                             CloseHandle (io.handle);
134 
135                         io.handle = INVALID_HANDLE_VALUE;
136                 }
137 
138                 /***************************************************************
139 
140                         Read a chunk of bytes from the file into the provided
141                         array. Returns the number of bytes read, or Eof where
142                         there is no further data.
143 
144                         Operates asynchronously where the hosting thread is
145                         configured in that manner.
146 
147                 ***************************************************************/
148 
149                 override size_t read (void[] dst)
150                 {
151                         DWORD bytes;
152 
153                         version(TangoRuntime)
154                         {
155                            if (! ReadFile (io.handle, dst.ptr, dst.length, &bytes, &io.asynch))
156                                  if ((bytes = wait (scheduler.Type.Read, bytes, timeout)) is Eof)
157                                       return Eof;
158                         }
159                         else
160                         {
161                            ReadFile (io.handle, dst.ptr, dst.length, &bytes, &io.asynch);
162                         }
163 
164                         // synchronous read of zero means Eof
165                         if (bytes is 0 && dst.length > 0)
166                             return Eof;
167 
168                         // update stream location?
169                         if (io.track)
170                            (*cast(long*) &io.asynch.Offset) += bytes;
171                         return bytes;
172                 }
173 
174                 /***************************************************************
175 
176                         Write a chunk of bytes to the file from the provided
177                         array. Returns the number of bytes written, or Eof if
178                         the output is no longer available.
179 
180                         Operates asynchronously where the hosting thread is
181                         configured in that manner.
182 
183                 ***************************************************************/
184 
185                 override size_t write (const(void)[] src)
186                 {
187                         DWORD bytes;
188 
189                         version(TangoRuntime)
190                         {
191                            if (! WriteFile (io.handle, src.ptr, src.length, &bytes, &io.asynch))
192                               if ((bytes = wait (scheduler.Type.Write, bytes, timeout)) is Eof)
193                                    return Eof;
194                         }
195                         else
196                         {
197                            WriteFile (io.handle, src.ptr, src.length, &bytes, &io.asynch);
198                         }
199 
200                         // update stream location?
201                         if (io.track)
202                            (*cast(long*) &io.asynch.Offset) += bytes;
203                         return bytes;
204                 }
205 
206                 /***************************************************************
207 
208                 ***************************************************************/
209 
210                 version(TangoRuntime)
211                 {
212                    protected final size_t wait (scheduler.Type type, uint bytes, uint timeout)
213                    {
214                            while (true)
215                                  {
216                                  auto code = GetLastError;
217                                  if (code is ERROR_HANDLE_EOF ||
218                                      code is ERROR_BROKEN_PIPE)
219                                      return Eof;
220 
221                                  if (scheduler)
222                                     {
223                                     if (code is ERROR_SUCCESS ||
224                                         code is ERROR_IO_PENDING ||
225                                         code is ERROR_IO_INCOMPLETE)
226                                        {
227                                        if (code is ERROR_IO_INCOMPLETE)
228                                            super.error ("timeout");
229 
230                                        io.task = cast(void*) tango.core.Thread.Fiber.getThis;
231                                        scheduler.await (io.handle, type, timeout);
232                                        if (GetOverlappedResult (io.handle, &io.asynch, &bytes, false))
233                                            return bytes;
234                                        }
235                                     else
236                                        error;
237                                     }
238                                  else
239                                     if (code is ERROR_SUCCESS)
240                                         return bytes;
241                                     else
242                                        error;
243                                  }
244 
245                            // should never get here
246                            assert(false);
247                    }
248                 }
249         }
250 
251 
252         /***********************************************************************
253 
254                  Unix-specific code.
255 
256         ***********************************************************************/
257 
258         version (Posix)
259         {
260                 protected int handle = -1;
261 
262                 /***************************************************************
263 
264                         Allow adjustment of standard IO handles.
265 
266                 ***************************************************************/
267 
268                 protected void reopen (Handle handle)
269                 {
270                         this.handle = handle;
271                 }
272 
273                 /***************************************************************
274 
275                         Return the underlying OS handle of this Conduit.
276 
277                 ***************************************************************/
278 
279                 @property final Handle fileHandle ()
280                 {
281                         return cast(Handle) handle;
282                 }
283 
284                 /***************************************************************
285 
286                         Release the underlying file.
287 
288                 ***************************************************************/
289 
290                 override void detach ()
291                 {
292                         if (handle >= 0)
293                            {
294                            //if (scheduler)
295                                // TODO Not supported on Posix
296                                // scheduler.close (handle, toString);
297                            posix.close (handle);
298                            }
299                         handle = -1;
300                 }
301 
302                 /***************************************************************
303 
304                         Read a chunk of bytes from the file into the provided
305                         array. Returns the number of bytes read, or Eof where
306                         there is no further data.
307 
308                 ***************************************************************/
309 
310                 override size_t read (void[] dst)
311                 {
312                         auto read = posix.read (handle, dst.ptr, dst.length);
313 
314                         if (read is -1)
315                             error();
316                         else
317                            if (read is 0 && dst.length > 0)
318                                return Eof;
319                         return read;
320                 }
321 
322                 /***************************************************************
323 
324                         Write a chunk of bytes to the file from the provided
325                         array. Returns the number of bytes written, or Eof if
326                         the output is no longer available.
327 
328                 ***************************************************************/
329 
330                 override size_t write (const(void)[] src)
331                 {
332                         size_t written = posix.write (handle, src.ptr, src.length);
333                         if (written is -1)
334                             error();
335                         return written;
336                 }
337         }
338 }