Uncommented beaudio code
[pwlib.git] / src / ptlib / unix / channel.cxx
blobe275ff12d57bdd8fc20d958823ad5c57e04e6765
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.39 2003/04/23 00:37:04 craigs
31 * More casts to avoid problems on MacOSX thanks to Shawn Hsiao
33 * Revision 1.38 2002/10/10 04:43:44 robertj
34 * VxWorks port, thanks Martijn Roest
36 * Revision 1.37 2002/06/09 16:53:17 rogerh
37 * The default for osError in PChannel::GetErrorText() is already specified in
38 * the prototype. (found by gcc 3.1)
40 * Revision 1.36 2002/01/26 23:58:15 craigs
41 * Changed for GCC 3.0 compatibility, thanks to manty@manty.net
43 * Revision 1.35 2001/11/27 02:20:20 robertj
44 * Fixed problem with a read ro write blocking until connect completed, it
45 * really should return an error as the caller is doing a bad thing.
47 * Revision 1.34 2001/09/20 05:23:39 robertj
48 * Fixed race deadlock problem in channel abort I/O function
50 * Revision 1.33 2001/09/18 05:56:03 robertj
51 * Fixed numerous problems with thread suspend/resume and signals handling.
53 * Revision 1.32 2001/09/11 03:39:19 robertj
54 * Improved error processing on high level protocol failures, usually
55 * caused by unexpected shut down of a socket.
57 * Revision 1.31 2001/09/10 03:03:36 robertj
58 * Major change to fix problem with error codes being corrupted in a
59 * PChannel when have simultaneous reads and writes in threads.
61 * Revision 1.30 2001/08/11 15:38:43 rogerh
62 * Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
64 * Revision 1.29 2001/06/30 06:59:07 yurik
65 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
67 * Revision 1.28 2001/03/20 06:44:25 robertj
68 * Lots of changes to fix the problems with terminating threads that are I/O
69 * blocked, especially when doing orderly shutdown of service via SIGTERM.
71 * Revision 1.27 2000/12/05 08:24:50 craigs
72 * Fixed problem with EINTR causing havoc
74 * Revision 1.26 2000/05/15 23:33:06 craigs
75 * Fixed problem where lastReadCount was not zeroed if no read occurred
77 * Revision 1.25 1999/03/02 05:41:59 robertj
78 * More BeOS changes
80 * Revision 1.24 1999/02/22 13:26:53 robertj
81 * BeOS port changes.
83 * Revision 1.23 1998/11/30 21:51:30 robertj
84 * New directory structure.
86 * Revision 1.22 1998/10/16 02:03:18 robertj
87 * Fixed error message output to include number on unknown errors.
89 * Revision 1.21 1998/10/16 01:15:38 craigs
90 * Added Yield to help with cooperative multithreading.
92 * Revision 1.20 1998/10/11 02:23:16 craigs
93 * Fixed problem with socket writes not correctly detecting EOF
95 * Revision 1.19 1998/09/24 04:12:09 robertj
96 * Added open software license.
98 * Revision 1.18 1998/08/27 01:06:30 robertj
99 * Fixed very strange link error with GNU C library v6.
101 * Revision 1.17 1998/05/25 10:03:26 robertj
102 * Fixed problem with socket/channel close and blocked threads.
104 * Revision 1.16 1998/03/26 05:01:12 robertj
105 * Added PMutex and PSyncPoint classes.
107 * Revision 1.15 1998/01/03 22:35:04 craigs
108 * Added PThread support
110 * Revision 1.14 1997/02/14 09:18:36 craigs
111 * Changed for PProcess::Current being a reference rather that a ptr
113 * Revision 1.13 1996/11/03 04:35:32 craigs
114 * Added PSocket::Read to fix recv/read problem
116 * Revision 1.12 1996/09/21 05:38:28 craigs
117 * Added indchan pragma
119 * Revision 1.11 1996/08/03 12:04:28 craigs
120 * Fixed problem with PChannel::Write terminating early
121 * Changed for new PChannel error reporting functions
123 * Revision 1.10 1996/05/25 06:06:33 craigs
124 * Sun4 fixes and updated for gcc 2.7.2
126 * Revision 1.9 1996/05/03 13:11:35 craigs
127 * More Sun4 fixes
129 * Revision 1.8 1996/05/02 12:01:23 craigs
130 * More Sun4 fixes
132 * Revision 1.7 1996/04/15 10:49:11 craigs
133 * Last build prior to release of MibMaster v1.0
135 * Revision 1.6 1996/01/26 11:09:42 craigs
136 * Fixed problem with blocking accepts and incorrect socket errors
138 * Revision 1.5 1995/10/15 12:56:54 craigs
139 * Multiple updates - split channel implementation into multiple files
141 * Revision 1.4 1995/07/09 00:35:43 craigs
142 * Latest and greatest omnibus change
144 * Revision 1.3 1995/02/15 20:28:14 craigs
145 * Removed sleep after pipe channel open
147 // Revision 1.2 1995/01/23 22:58:01 craigs
148 // Changes for HPUX and Sun 4
152 #pragma implementation "channel.h"
153 #pragma implementation "indchan.h"
155 #include <ptlib.h>
156 #include <sys/ioctl.h>
159 #include "../common/pchannel.cxx"
162 #ifdef P_NEED_IOSTREAM_MUTEX
163 static PMutex iostreamMutex;
164 #define IOSTREAM_MUTEX_WAIT() iostreamMutex.Wait();
165 #define IOSTREAM_MUTEX_SIGNAL() iostreamMutex.Signal();
166 #else
167 #define IOSTREAM_MUTEX_WAIT()
168 #define IOSTREAM_MUTEX_SIGNAL()
169 #endif
172 void PChannel::Construct()
174 os_handle = -1;
175 px_lastBlockType = PXReadBlock;
176 px_readThread = NULL;
177 px_writeThread = NULL;
181 ///////////////////////////////////////////////////////////////////////////////
183 // PChannel::PXSetIOBlock
184 // This function is used to perform IO blocks.
185 // If the return value is FALSE, then the select call either
186 // returned an error or a timeout occurred. The member variable lastError
187 // can be used to determine which error occurred
190 BOOL PChannel::PXSetIOBlock(PXBlockType type, const PTimeInterval & timeout)
192 ErrorGroup group;
193 switch (type) {
194 case PXReadBlock :
195 group = LastReadError;
196 break;
197 case PXWriteBlock :
198 group = LastWriteError;
199 break;
200 default :
201 group = LastGeneralError;
204 if (os_handle < 0)
205 return SetErrorValues(NotOpen, EBADF, group);
207 PThread * blockedThread = PThread::Current();
210 PWaitAndSignal mutex(px_threadMutex);
211 switch (type) {
212 case PXWriteBlock :
213 if (px_readThread != NULL && px_lastBlockType != PXReadBlock)
214 return SetErrorValues(DeviceInUse, EBUSY, LastReadError);
216 PTRACE(4, "PWLib\tBlocking on write.");
217 px_writeMutex.Wait();
218 px_writeThread = blockedThread;
219 break;
221 case PXReadBlock :
222 PAssert(px_readThread == NULL || px_lastBlockType != PXReadBlock,
223 "Attempt to do simultaneous reads from multiple threads.");
224 // Fall into default case
226 default :
227 if (px_readThread != NULL)
228 return SetErrorValues(DeviceInUse, EBUSY, LastReadError);
229 px_readThread = blockedThread;
230 px_lastBlockType = type;
234 int stat = blockedThread->PXBlockOnIO(os_handle, type, timeout);
236 px_threadMutex.Wait();
237 if (type != PXWriteBlock) {
238 px_lastBlockType = PXReadBlock;
239 px_readThread = NULL;
241 else {
242 px_writeThread = NULL;
243 px_writeMutex.Signal();
245 px_threadMutex.Signal();
247 // if select returned < 0, then convert errno into lastError and return FALSE
248 if (stat < 0)
249 return ConvertOSError(stat, group);
251 // if the select succeeded, then return TRUE
252 if (stat > 0)
253 return TRUE;
255 // otherwise, a timeout occurred so return FALSE
256 return SetErrorValues(Timeout, ETIMEDOUT, group);
260 BOOL PChannel::Read(void * buf, PINDEX len)
262 lastReadCount = 0;
264 if (os_handle < 0)
265 return SetErrorValues(NotOpen, EBADF, LastReadError);
267 if (!PXSetIOBlock(PXReadBlock, readTimeout))
268 return FALSE;
270 if (ConvertOSError(lastReadCount = ::read(os_handle, buf, len), LastReadError))
271 return lastReadCount > 0;
273 lastReadCount = 0;
274 return FALSE;
278 BOOL PChannel::Write(const void * buf, PINDEX len)
280 // if the os_handle isn't open, no can do
281 if (os_handle < 0)
282 return SetErrorValues(NotOpen, EBADF, LastWriteError);
284 // flush the buffer before doing a write
285 IOSTREAM_MUTEX_WAIT();
286 flush();
287 IOSTREAM_MUTEX_SIGNAL();
289 lastWriteCount = 0;
291 while (len > 0) {
293 int result;
294 while ((result = ::write(os_handle, ((char *)buf)+lastWriteCount, len)) < 0) {
295 if (errno != EWOULDBLOCK)
296 return ConvertOSError(-1, LastWriteError);
298 if (!PXSetIOBlock(PXWriteBlock, writeTimeout))
299 return FALSE;
302 lastWriteCount += result;
303 len -= result;
306 #if !defined(P_PTHREADS) && !defined(P_MAC_MPTHREADS)
307 PThread::Yield(); // Starvation prevention
308 #endif
310 // Reset all the errors.
311 return ConvertOSError(0, LastWriteError);
314 BOOL PChannel::Close()
316 if (os_handle < 0)
317 return SetErrorValues(NotOpen, EBADF);
319 return ConvertOSError(PXClose());
323 static void AbortIO(PThread * & thread, PMutex & mutex)
325 mutex.Wait();
326 if (thread != NULL)
327 thread->PXAbortBlock();
328 mutex.Signal();
330 while (thread != NULL)
331 PThread::Yield();
334 int PChannel::PXClose()
336 if (os_handle < 0)
337 return -1;
339 // make sure we don't have any problems
340 IOSTREAM_MUTEX_WAIT();
341 flush();
342 int handle = os_handle;
343 os_handle = -1;
344 IOSTREAM_MUTEX_SIGNAL();
346 #if !defined(P_PTHREADS) && !defined(BE_THREADS) && !defined(P_MAC_MPTHREADS) && !defined(VX_TASKS)
347 // abort any I/O block using this os_handle
348 PProcess::Current().PXAbortIOBlock(handle);
350 #ifndef BE_BONELESS
351 DWORD cmd = 0;
352 ::ioctl(handle, FIONBIO, &cmd);
353 #endif
354 #endif
356 AbortIO(px_readThread, px_threadMutex);
357 AbortIO(px_writeThread, px_threadMutex);
359 int stat;
360 do {
361 stat = ::close(handle);
362 } while (stat == -1 && errno == EINTR);
364 return stat;
367 PString PChannel::GetErrorText(Errors normalisedError, int osError /* =0 */)
369 if (osError == 0) {
370 if (normalisedError == NoError)
371 return PString();
373 static int const errors[NumNormalisedErrors] = {
374 0, ENOENT, EEXIST, ENOSPC, EACCES, EBUSY, EINVAL, ENOMEM, EBADF, EAGAIN, EINTR,
375 EMSGSIZE, EIO, 0x1000000
377 osError = errors[normalisedError];
380 if (osError == 0x1000000)
381 return "High level protocol failure";
383 const char * err = strerror(osError);
384 if (err != NULL)
385 return err;
387 return psprintf("Unknown error %d", osError);
391 BOOL PChannel::ConvertOSError(int err, Errors & lastError, int & osError)
394 osError = (err >= 0) ? 0 : errno;
396 switch (osError) {
397 case 0 :
398 lastError = NoError;
399 return TRUE;
401 case EMSGSIZE:
402 lastError = BufferTooSmall;
403 break;
405 case EBADF: // will get EBADF if a read/write occurs after closing. This must return Interrupted
406 case EINTR:
407 lastError = Interrupted;
408 break;
410 case EEXIST:
411 lastError = FileExists;
412 break;
414 case EISDIR:
415 case EROFS:
416 case EACCES:
417 case EPERM:
418 lastError = AccessDenied;
419 break;
421 #ifndef __BEOS__
422 case ETXTBSY:
423 lastError = DeviceInUse;
424 break;
425 #endif
427 case EFAULT:
428 case ELOOP:
429 case EINVAL:
430 lastError = BadParameter;
431 break;
433 case ENOENT :
434 case ENAMETOOLONG:
435 case ENOTDIR:
436 lastError = NotFound;
437 break;
439 case EMFILE:
440 case ENFILE:
441 case ENOMEM :
442 lastError = NoMemory;
443 break;
445 case ENOSPC:
446 lastError = DiskFull;
447 break;
449 default :
450 lastError = Miscellaneous;
451 break;
453 return FALSE;
457 ///////////////////////////////////////////////////////////////////////////////