84
84
//! the test suite passing (the suite is in libstd), and that's good enough for
85
85
//! me!
86
86
87
- use std:: c_str:: CString ;
88
87
use libc;
88
+ use std:: c_str:: CString ;
89
+ use std:: intrinsics;
90
+ use std:: io;
89
91
use std:: os:: win32:: as_utf16_p;
92
+ use std:: os;
90
93
use std:: ptr;
91
94
use std:: rt:: rtio;
92
95
use std:: sync:: arc:: UnsafeArc ;
93
- use std:: intrinsics;
96
+ use std:: sync:: atomics;
97
+ use std:: unstable:: mutex;
94
98
95
99
use super :: IoResult ;
96
100
use super :: c;
@@ -124,6 +128,20 @@ impl Drop for Event {
124
128
125
129
struct Inner {
126
130
handle : libc:: HANDLE ,
131
+ lock : mutex:: NativeMutex ,
132
+ read_closed : atomics:: AtomicBool ,
133
+ write_closed : atomics:: AtomicBool ,
134
+ }
135
+
136
+ impl Inner {
137
+ fn new ( handle : libc:: HANDLE ) -> Inner {
138
+ Inner {
139
+ handle : handle,
140
+ lock : unsafe { mutex:: NativeMutex :: new ( ) } ,
141
+ read_closed : atomics:: AtomicBool :: new ( false ) ,
142
+ write_closed : atomics:: AtomicBool :: new ( false ) ,
143
+ }
144
+ }
127
145
}
128
146
129
147
impl Drop for Inner {
@@ -218,7 +236,7 @@ impl UnixStream {
218
236
loop {
219
237
match UnixStream :: try_connect ( p) {
220
238
Some ( handle) => {
221
- let inner = Inner { handle : handle } ;
239
+ let inner = Inner :: new ( handle) ;
222
240
let mut mode = libc:: PIPE_TYPE_BYTE |
223
241
libc:: PIPE_READMODE_BYTE |
224
242
libc:: PIPE_WAIT ;
@@ -275,6 +293,24 @@ impl UnixStream {
275
293
}
276
294
277
295
fn handle ( & self ) -> libc:: HANDLE { unsafe { ( * self . inner . get ( ) ) . handle } }
296
+
297
+ fn read_closed ( & self ) -> bool {
298
+ unsafe { ( * self . inner . get ( ) ) . read_closed . load ( atomics:: SeqCst ) }
299
+ }
300
+
301
+ fn write_closed ( & self ) -> bool {
302
+ unsafe { ( * self . inner . get ( ) ) . write_closed . load ( atomics:: SeqCst ) }
303
+ }
304
+
305
+ fn cancel_io ( & self ) -> IoResult < ( ) > {
306
+ match unsafe { c:: CancelIoEx ( self . handle ( ) , ptr:: mut_null ( ) ) } {
307
+ 0 if os:: errno ( ) == libc:: ERROR_NOT_FOUND as uint => {
308
+ Ok ( ( ) )
309
+ }
310
+ 0 => Err ( super :: last_error ( ) ) ,
311
+ _ => Ok ( ( ) )
312
+ }
313
+ }
278
314
}
279
315
280
316
impl rtio:: RtioPipe for UnixStream {
@@ -287,31 +323,60 @@ impl rtio::RtioPipe for UnixStream {
287
323
let mut overlapped: libc:: OVERLAPPED = unsafe { intrinsics:: init ( ) } ;
288
324
overlapped. hEvent = self . read . get_ref ( ) . handle ( ) ;
289
325
326
+ // Pre-flight check to see if the reading half has been closed. This
327
+ // must be done before issuing the ReadFile request, but after we
328
+ // acquire the lock.
329
+ //
330
+ // See comments in close_read() about why this lock is necessary.
331
+ let guard = unsafe { ( * self . inner . get ( ) ) . lock . lock ( ) } ;
332
+ if self . read_closed ( ) {
333
+ return Err ( io:: standard_error ( io:: EndOfFile ) )
334
+ }
335
+
336
+ // Issue a nonblocking requests, succeeding quickly if it happened to
337
+ // succeed.
290
338
let ret = unsafe {
291
339
libc:: ReadFile ( self . handle ( ) ,
292
340
buf. as_ptr ( ) as libc:: LPVOID ,
293
341
buf. len ( ) as libc:: DWORD ,
294
342
& mut bytes_read,
295
343
& mut overlapped)
296
344
} ;
297
- if ret == 0 {
298
- let err = unsafe { libc:: GetLastError ( ) } ;
299
- if err == libc:: ERROR_IO_PENDING as libc:: DWORD {
300
- let ret = unsafe {
301
- libc:: GetOverlappedResult ( self . handle ( ) ,
302
- & mut overlapped,
303
- & mut bytes_read,
304
- libc:: TRUE )
305
- } ;
306
- if ret == 0 {
307
- return Err ( super :: last_error ( ) )
308
- }
309
- } else {
345
+ if ret != 0 { return Ok ( bytes_read as uint ) }
346
+
347
+ // If our errno doesn't say that the I/O is pending, then we hit some
348
+ // legitimate error and reeturn immediately.
349
+ if os:: errno ( ) != libc:: ERROR_IO_PENDING as uint {
350
+ return Err ( super :: last_error ( ) )
351
+ }
352
+
353
+ // Now that we've issued a successful nonblocking request, we need to
354
+ // wait for it to finish. This can all be done outside the lock because
355
+ // we'll see any invocation of CancelIoEx. We also call this in a loop
356
+ // because we're woken up if the writing half is closed, we just need to
357
+ // realize that the reading half wasn't closed and we go right back to
358
+ // sleep.
359
+ drop ( guard) ;
360
+ loop {
361
+ let ret = unsafe {
362
+ libc:: GetOverlappedResult ( self . handle ( ) ,
363
+ & mut overlapped,
364
+ & mut bytes_read,
365
+ libc:: TRUE )
366
+ } ;
367
+ // If we succeeded, or we failed for some reason other than
368
+ // CancelIoEx, return immediately
369
+ if ret != 0 { return Ok ( bytes_read as uint ) }
370
+ if os:: errno ( ) != libc:: ERROR_OPERATION_ABORTED as uint {
310
371
return Err ( super :: last_error ( ) )
311
372
}
312
- }
313
373
314
- Ok ( bytes_read as uint )
374
+ // If the reading half is now closed, then we're done. If we woke up
375
+ // because the writing half was closed, keep trying.
376
+ if self . read_closed ( ) {
377
+ return Err ( io:: standard_error ( io:: EndOfFile ) )
378
+ }
379
+ }
315
380
}
316
381
317
382
fn write ( & mut self , buf : & [ u8 ] ) -> IoResult < ( ) > {
@@ -325,27 +390,47 @@ impl rtio::RtioPipe for UnixStream {
325
390
326
391
while offset < buf. len ( ) {
327
392
let mut bytes_written = 0 ;
393
+
394
+ // This sequence below is quite similar to the one found in read().
395
+ // Some careful looping is done to ensure that if close_write() is
396
+ // invoked we bail out early, and if close_read() is invoked we keep
397
+ // going after we woke up.
398
+ //
399
+ // See comments in close_read() about why this lock is necessary.
400
+ let guard = unsafe { ( * self . inner . get ( ) ) . lock . lock ( ) } ;
401
+ if self . write_closed ( ) {
402
+ return Err ( io:: standard_error ( io:: BrokenPipe ) )
403
+ }
328
404
let ret = unsafe {
329
405
libc:: WriteFile ( self . handle ( ) ,
330
406
buf. slice_from ( offset) . as_ptr ( ) as libc:: LPVOID ,
331
407
( buf. len ( ) - offset) as libc:: DWORD ,
332
408
& mut bytes_written,
333
409
& mut overlapped)
334
410
} ;
411
+ drop ( guard) ;
412
+
335
413
if ret == 0 {
336
- let err = unsafe { libc:: GetLastError ( ) } ;
337
- if err == libc:: ERROR_IO_PENDING as libc:: DWORD {
338
- let ret = unsafe {
339
- libc:: GetOverlappedResult ( self . handle ( ) ,
340
- & mut overlapped,
341
- & mut bytes_written,
342
- libc:: TRUE )
343
- } ;
344
- if ret == 0 {
414
+ if os:: errno ( ) != libc:: ERROR_IO_PENDING as uint {
415
+ return Err ( super :: last_error ( ) )
416
+ }
417
+ let ret = unsafe {
418
+ libc:: GetOverlappedResult ( self . handle ( ) ,
419
+ & mut overlapped,
420
+ & mut bytes_written,
421
+ libc:: TRUE )
422
+ } ;
423
+ // If we weren't aborted, this was a legit error, if we were
424
+ // aborted, then check to see if the write half was actually
425
+ // closed or whether we woke up from the read half closing.
426
+ if ret == 0 {
427
+ if os:: errno ( ) != libc:: ERROR_OPERATION_ABORTED as uint {
345
428
return Err ( super :: last_error ( ) )
346
429
}
347
- } else {
348
- return Err ( super :: last_error ( ) )
430
+ if self . write_closed ( ) {
431
+ return Err ( io:: standard_error ( io:: BrokenPipe ) )
432
+ }
433
+ continue ; // retry
349
434
}
350
435
}
351
436
offset += bytes_written as uint ;
@@ -360,6 +445,36 @@ impl rtio::RtioPipe for UnixStream {
360
445
write : None ,
361
446
} as Box < rtio:: RtioPipe : Send >
362
447
}
448
+
449
+ fn close_read ( & mut self ) -> IoResult < ( ) > {
450
+ // On windows, there's no actual shutdown() method for pipes, so we're
451
+ // forced to emulate the behavior manually at the application level. To
452
+ // do this, we need to both cancel any pending requests, as well as
453
+ // prevent all future requests from succeeding. These two operations are
454
+ // not atomic with respect to one another, so we must use a lock to do
455
+ // so.
456
+ //
457
+ // The read() code looks like:
458
+ //
459
+ // 1. Make sure the pipe is still open
460
+ // 2. Submit a read request
461
+ // 3. Wait for the read request to finish
462
+ //
463
+ // The race this lock is preventing is if another thread invokes
464
+ // close_read() between steps 1 and 2. By atomically executing steps 1
465
+ // and 2 with a lock with respect to close_read(), we're guaranteed that
466
+ // no thread will erroneously sit in a read forever.
467
+ let _guard = unsafe { ( * self . inner . get ( ) ) . lock . lock ( ) } ;
468
+ unsafe { ( * self . inner . get ( ) ) . read_closed . store ( true , atomics:: SeqCst ) }
469
+ self . cancel_io ( )
470
+ }
471
+
472
+ fn close_write ( & mut self ) -> IoResult < ( ) > {
473
+ // see comments in close_read() for why this lock is necessary
474
+ let _guard = unsafe { ( * self . inner . get ( ) ) . lock . lock ( ) } ;
475
+ unsafe { ( * self . inner . get ( ) ) . write_closed . store ( true , atomics:: SeqCst ) }
476
+ self . cancel_io ( )
477
+ }
363
478
}
364
479
365
480
////////////////////////////////////////////////////////////////////////////////
@@ -520,7 +635,7 @@ impl UnixAcceptor {
520
635
521
636
// Transfer ownership of our handle into this stream
522
637
Ok ( UnixStream {
523
- inner : UnsafeArc :: new ( Inner { handle : handle } ) ,
638
+ inner : UnsafeArc :: new ( Inner :: new ( handle) ) ,
524
639
read : None ,
525
640
write : None ,
526
641
} )
0 commit comments