Fixed DevStudio 2003 build with memory check code.
[pwlib.git] / src / ptlib / unix / channel.cxx
blob49e66ae68cce77635ada807759cfcf8a810aef23
1 /*
2 * channel.cxx
4 * I/O channel classes implementation
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
29 * $Log$
30 * Revision 1.41 2005/08/05 20:41:42 csoutheren
31 * Added unix support for scattered read/write
33 * Revision 1.40 2004/04/27 04:37:51 rjongbloed
34 * Fixed ability to break of a PSocket::Select call under linux when a socket
35 * is closed by another thread.
37 * Revision 1.39 2003/04/23 00:37:04 craigs
38 * More casts to avoid problems on MacOSX thanks to Shawn Hsiao
40 * Revision 1.38 2002/10/10 04:43:44 robertj
41 * VxWorks port, thanks Martijn Roest
43 * Revision 1.37 2002/06/09 16:53:17 rogerh
44 * The default for osError in PChannel::GetErrorText() is already specified in
45 * the prototype. (found by gcc 3.1)
47 * Revision 1.36 2002/01/26 23:58:15 craigs
48 * Changed for GCC 3.0 compatibility, thanks to manty@manty.net
50 * Revision 1.35 2001/11/27 02:20:20 robertj
51 * Fixed problem with a read ro write blocking until connect completed, it
52 * really should return an error as the caller is doing a bad thing.
54 * Revision 1.34 2001/09/20 05:23:39 robertj
55 * Fixed race deadlock problem in channel abort I/O function
57 * Revision 1.33 2001/09/18 05:56:03 robertj
58 * Fixed numerous problems with thread suspend/resume and signals handling.
60 * Revision 1.32 2001/09/11 03:39:19 robertj
61 * Improved error processing on high level protocol failures, usually
62 * caused by unexpected shut down of a socket.
64 * Revision 1.31 2001/09/10 03:03:36 robertj
65 * Major change to fix problem with error codes being corrupted in a
66 * PChannel when have simultaneous reads and writes in threads.
68 * Revision 1.30 2001/08/11 15:38:43 rogerh
69 * Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
71 * Revision 1.29 2001/06/30 06:59:07 yurik
72 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
74 * Revision 1.28 2001/03/20 06:44:25 robertj
75 * Lots of changes to fix the problems with terminating threads that are I/O
76 * blocked, especially when doing orderly shutdown of service via SIGTERM.
78 * Revision 1.27 2000/12/05 08:24:50 craigs
79 * Fixed problem with EINTR causing havoc
81 * Revision 1.26 2000/05/15 23:33:06 craigs
82 * Fixed problem where lastReadCount was not zeroed if no read occurred
84 * Revision 1.25 1999/03/02 05:41:59 robertj
85 * More BeOS changes
87 * Revision 1.24 1999/02/22 13:26:53 robertj
88 * BeOS port changes.
90 * Revision 1.23 1998/11/30 21:51:30 robertj
91 * New directory structure.
93 * Revision 1.22 1998/10/16 02:03:18 robertj
94 * Fixed error message output to include number on unknown errors.
96 * Revision 1.21 1998/10/16 01:15:38 craigs
97 * Added Yield to help with cooperative multithreading.
99 * Revision 1.20 1998/10/11 02:23:16 craigs
100 * Fixed problem with socket writes not correctly detecting EOF
102 * Revision 1.19 1998/09/24 04:12:09 robertj
103 * Added open software license.
105 * Revision 1.18 1998/08/27 01:06:30 robertj
106 * Fixed very strange link error with GNU C library v6.
108 * Revision 1.17 1998/05/25 10:03:26 robertj
109 * Fixed problem with socket/channel close and blocked threads.
111 * Revision 1.16 1998/03/26 05:01:12 robertj
112 * Added PMutex and PSyncPoint classes.
114 * Revision 1.15 1998/01/03 22:35:04 craigs
115 * Added PThread support
117 * Revision 1.14 1997/02/14 09:18:36 craigs
118 * Changed for PProcess::Current being a reference rather that a ptr
120 * Revision 1.13 1996/11/03 04:35:32 craigs
121 * Added PSocket::Read to fix recv/read problem
123 * Revision 1.12 1996/09/21 05:38:28 craigs
124 * Added indchan pragma
126 * Revision 1.11 1996/08/03 12:04:28 craigs
127 * Fixed problem with PChannel::Write terminating early
128 * Changed for new PChannel error reporting functions
130 * Revision 1.10 1996/05/25 06:06:33 craigs
131 * Sun4 fixes and updated for gcc 2.7.2
133 * Revision 1.9 1996/05/03 13:11:35 craigs
134 * More Sun4 fixes
136 * Revision 1.8 1996/05/02 12:01:23 craigs
137 * More Sun4 fixes
139 * Revision 1.7 1996/04/15 10:49:11 craigs
140 * Last build prior to release of MibMaster v1.0
142 * Revision 1.6 1996/01/26 11:09:42 craigs
143 * Fixed problem with blocking accepts and incorrect socket errors
145 * Revision 1.5 1995/10/15 12:56:54 craigs
146 * Multiple updates - split channel implementation into multiple files
148 * Revision 1.4 1995/07/09 00:35:43 craigs
149 * Latest and greatest omnibus change
151 * Revision 1.3 1995/02/15 20:28:14 craigs
152 * Removed sleep after pipe channel open
154 // Revision 1.2 1995/01/23 22:58:01 craigs
155 // Changes for HPUX and Sun 4
159 #pragma implementation "channel.h"
160 #pragma implementation "indchan.h"
162 #include <ptlib.h>
163 #include <sys/ioctl.h>
166 #include "../common/pchannel.cxx"
169 #ifdef P_NEED_IOSTREAM_MUTEX
170 static PMutex iostreamMutex;
171 #define IOSTREAM_MUTEX_WAIT() iostreamMutex.Wait();
172 #define IOSTREAM_MUTEX_SIGNAL() iostreamMutex.Signal();
173 #else
174 #define IOSTREAM_MUTEX_WAIT()
175 #define IOSTREAM_MUTEX_SIGNAL()
176 #endif
179 void PChannel::Construct()
181 os_handle = -1;
182 px_lastBlockType = PXReadBlock;
183 px_readThread = NULL;
184 px_writeThread = NULL;
185 px_selectThread = NULL;
189 ///////////////////////////////////////////////////////////////////////////////
191 // PChannel::PXSetIOBlock
192 // This function is used to perform IO blocks.
193 // If the return value is FALSE, then the select call either
194 // returned an error or a timeout occurred. The member variable lastError
195 // can be used to determine which error occurred
198 BOOL PChannel::PXSetIOBlock(PXBlockType type, const PTimeInterval & timeout)
200 ErrorGroup group;
201 switch (type) {
202 case PXReadBlock :
203 group = LastReadError;
204 break;
205 case PXWriteBlock :
206 group = LastWriteError;
207 break;
208 default :
209 group = LastGeneralError;
212 if (os_handle < 0)
213 return SetErrorValues(NotOpen, EBADF, group);
215 PThread * blockedThread = PThread::Current();
218 PWaitAndSignal mutex(px_threadMutex);
219 switch (type) {
220 case PXWriteBlock :
221 if (px_readThread != NULL && px_lastBlockType != PXReadBlock)
222 return SetErrorValues(DeviceInUse, EBUSY, LastReadError);
224 PTRACE(4, "PWLib\tBlocking on write.");
225 px_writeMutex.Wait();
226 px_writeThread = blockedThread;
227 break;
229 case PXReadBlock :
230 PAssert(px_readThread == NULL || px_lastBlockType != PXReadBlock,
231 "Attempt to do simultaneous reads from multiple threads.");
232 // Fall into default case
234 default :
235 if (px_readThread != NULL)
236 return SetErrorValues(DeviceInUse, EBUSY, LastReadError);
237 px_readThread = blockedThread;
238 px_lastBlockType = type;
242 int stat = blockedThread->PXBlockOnIO(os_handle, type, timeout);
244 px_threadMutex.Wait();
245 if (type != PXWriteBlock) {
246 px_lastBlockType = PXReadBlock;
247 px_readThread = NULL;
249 else {
250 px_writeThread = NULL;
251 px_writeMutex.Signal();
253 px_threadMutex.Signal();
255 // if select returned < 0, then convert errno into lastError and return FALSE
256 if (stat < 0)
257 return ConvertOSError(stat, group);
259 // if the select succeeded, then return TRUE
260 if (stat > 0)
261 return TRUE;
263 // otherwise, a timeout occurred so return FALSE
264 return SetErrorValues(Timeout, ETIMEDOUT, group);
268 BOOL PChannel::Read(void * buf, PINDEX len)
270 lastReadCount = 0;
272 if (os_handle < 0)
273 return SetErrorValues(NotOpen, EBADF, LastReadError);
275 if (!PXSetIOBlock(PXReadBlock, readTimeout))
276 return FALSE;
278 if (ConvertOSError(lastReadCount = ::read(os_handle, buf, len), LastReadError))
279 return lastReadCount > 0;
281 lastReadCount = 0;
282 return FALSE;
286 BOOL PChannel::Write(const void * buf, PINDEX len)
288 // if the os_handle isn't open, no can do
289 if (os_handle < 0)
290 return SetErrorValues(NotOpen, EBADF, LastWriteError);
292 // flush the buffer before doing a write
293 IOSTREAM_MUTEX_WAIT();
294 flush();
295 IOSTREAM_MUTEX_SIGNAL();
297 lastWriteCount = 0;
299 while (len > 0) {
301 int result;
302 while ((result = ::write(os_handle, ((char *)buf)+lastWriteCount, len)) < 0) {
303 if (errno != EWOULDBLOCK)
304 return ConvertOSError(-1, LastWriteError);
306 if (!PXSetIOBlock(PXWriteBlock, writeTimeout))
307 return FALSE;
310 lastWriteCount += result;
311 len -= result;
314 #if !defined(P_PTHREADS) && !defined(P_MAC_MPTHREADS)
315 PThread::Yield(); // Starvation prevention
316 #endif
318 // Reset all the errors.
319 return ConvertOSError(0, LastWriteError);
322 #ifdef P_HAS_RECVMSG
324 BOOL PChannel::Read(const VectorOfSlice & slices)
326 lastReadCount = 0;
328 if (os_handle < 0)
329 return SetErrorValues(NotOpen, EBADF, LastReadError);
331 if (!PXSetIOBlock(PXReadBlock, readTimeout))
332 return FALSE;
334 if (ConvertOSError(lastReadCount = ::readv(os_handle, &slices[0], slices.size()), LastReadError))
335 return lastReadCount > 0;
337 lastReadCount = 0;
338 return FALSE;
341 BOOL PChannel::Write(const VectorOfSlice & slices)
343 // if the os_handle isn't open, no can do
344 if (os_handle < 0)
345 return SetErrorValues(NotOpen, EBADF, LastWriteError);
347 // flush the buffer before doing a write
348 IOSTREAM_MUTEX_WAIT();
349 flush();
350 IOSTREAM_MUTEX_SIGNAL();
352 int result;
353 while ((result = ::writev(os_handle, &slices[0], slices.size())) < 0) {
354 if (errno != EWOULDBLOCK)
355 return ConvertOSError(-1, LastWriteError);
357 if (!PXSetIOBlock(PXWriteBlock, writeTimeout))
358 return FALSE;
361 #if !defined(P_PTHREADS) && !defined(P_MAC_MPTHREADS)
362 PThread::Yield(); // Starvation prevention
363 #endif
365 // Reset all the errors.
366 return ConvertOSError(0, LastWriteError);
369 #endif
371 BOOL PChannel::Close()
373 if (os_handle < 0)
374 return SetErrorValues(NotOpen, EBADF);
376 return ConvertOSError(PXClose());
380 static void AbortIO(PThread * & thread, PMutex & mutex)
382 mutex.Wait();
383 if (thread != NULL)
384 thread->PXAbortBlock();
385 mutex.Signal();
387 while (thread != NULL)
388 PThread::Yield();
391 int PChannel::PXClose()
393 if (os_handle < 0)
394 return -1;
396 PTRACE(6, "PWLib\tClosing channel, fd=" << os_handle);
398 // make sure we don't have any problems
399 IOSTREAM_MUTEX_WAIT();
400 flush();
401 int handle = os_handle;
402 os_handle = -1;
403 IOSTREAM_MUTEX_SIGNAL();
405 #if !defined(P_PTHREADS) && !defined(BE_THREADS) && !defined(P_MAC_MPTHREADS) && !defined(VX_TASKS)
406 // abort any I/O block using this os_handle
407 PProcess::Current().PXAbortIOBlock(handle);
409 #ifndef BE_BONELESS
410 DWORD cmd = 0;
411 ::ioctl(handle, FIONBIO, &cmd);
412 #endif
413 #endif
415 AbortIO(px_readThread, px_threadMutex);
416 AbortIO(px_writeThread, px_threadMutex);
417 AbortIO(px_selectThread, px_threadMutex);
419 int stat;
420 do {
421 stat = ::close(handle);
422 } while (stat == -1 && errno == EINTR);
424 return stat;
427 PString PChannel::GetErrorText(Errors normalisedError, int osError /* =0 */)
429 if (osError == 0) {
430 if (normalisedError == NoError)
431 return PString();
433 static int const errors[NumNormalisedErrors] = {
434 0, ENOENT, EEXIST, ENOSPC, EACCES, EBUSY, EINVAL, ENOMEM, EBADF, EAGAIN, EINTR,
435 EMSGSIZE, EIO, 0x1000000
437 osError = errors[normalisedError];
440 if (osError == 0x1000000)
441 return "High level protocol failure";
443 const char * err = strerror(osError);
444 if (err != NULL)
445 return err;
447 return psprintf("Unknown error %d", osError);
451 BOOL PChannel::ConvertOSError(int err, Errors & lastError, int & osError)
454 osError = (err >= 0) ? 0 : errno;
456 switch (osError) {
457 case 0 :
458 lastError = NoError;
459 return TRUE;
461 case EMSGSIZE:
462 lastError = BufferTooSmall;
463 break;
465 case EBADF: // will get EBADF if a read/write occurs after closing. This must return Interrupted
466 case EINTR:
467 lastError = Interrupted;
468 break;
470 case EEXIST:
471 lastError = FileExists;
472 break;
474 case EISDIR:
475 case EROFS:
476 case EACCES:
477 case EPERM:
478 lastError = AccessDenied;
479 break;
481 #ifndef __BEOS__
482 case ETXTBSY:
483 lastError = DeviceInUse;
484 break;
485 #endif
487 case EFAULT:
488 case ELOOP:
489 case EINVAL:
490 lastError = BadParameter;
491 break;
493 case ENOENT :
494 case ENAMETOOLONG:
495 case ENOTDIR:
496 lastError = NotFound;
497 break;
499 case EMFILE:
500 case ENFILE:
501 case ENOMEM :
502 lastError = NoMemory;
503 break;
505 case ENOSPC:
506 lastError = DiskFull;
507 break;
509 default :
510 lastError = Miscellaneous;
511 break;
513 return FALSE;
517 ///////////////////////////////////////////////////////////////////////////////