diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 0d7efc3dd5450900d7917c29b75c1a4408284476..380b3706f8ab28045aaf41f150bf4e1ed82b73c6 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1543,7 +1543,11 @@ static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
         status = wine_server_call( req );
         wait_handle = wine_server_ptr_handle( reply->wait );
         options     = reply->options;
-        if (status != STATUS_PENDING) io->Information = wine_server_reply_size( reply );
+        if (wait_handle && status != STATUS_PENDING)
+        {
+            io->u.Status    = status;
+            io->Information = wine_server_reply_size( reply );
+        }
     }
     SERVER_END_REQ;
 
@@ -1557,10 +1561,8 @@ static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
     {
         NtWaitForSingleObject( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), NULL );
         status = io->u.Status;
-        NtClose( wait_handle );
     }
 
-    if (status != STATUS_PENDING && code != FSCTL_PIPE_LISTEN) io->u.Status = status;
     return status;
 }
 
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c
index c0a785b0f40009d9411600e11a35a282b05ce148..f567e616a2afc5ad8226bd811386aa895c0e968f 100644
--- a/dlls/ntdll/tests/pipe.c
+++ b/dlls/ntdll/tests/pipe.c
@@ -724,7 +724,7 @@ static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
     ok( !reserved, "reserved is not 0: %x\n", reserved );
 }
 
-static void test_peek(HANDLE pipe)
+static void test_peek(HANDLE pipe, BOOL is_msgmode)
 {
     FILE_PIPE_PEEK_BUFFER buf;
     IO_STATUS_BLOCK iosb;
@@ -743,7 +743,7 @@ static void test_peek(HANDLE pipe)
     ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status);
     ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable);
     ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status);
-    todo_wine
+    todo_wine_if(!is_msgmode)
     ok(is_signaled(event), "event is not signaled\n");
 
     CloseHandle(event);
@@ -855,7 +855,7 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type)
     ret = WriteFile( write, buffer, 1, &written, NULL );
     ok(ret && written == 1, "WriteFile error %d\n", GetLastError());
 
-    test_peek(read);
+    test_peek(read, pipe_type & PIPE_TYPE_MESSAGE);
 
     status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL );
     ok( status == STATUS_SUCCESS, "wrong status %x\n", status );
diff --git a/server/device.c b/server/device.c
index 4c7aa16cf2a7bf86ecbfd380215b527a62b3ed88..c19905bf30a0035088626fe967589df47ceb02c9 100644
--- a/server/device.c
+++ b/server/device.c
@@ -177,7 +177,7 @@ static enum server_fd_type device_file_get_fd_type( struct fd *fd );
 static int device_file_read( struct fd *fd, struct async *async, file_pos_t pos );
 static int device_file_write( struct fd *fd, struct async *async, file_pos_t pos );
 static obj_handle_t device_file_flush( struct fd *fd, struct async *async );
-static obj_handle_t device_file_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+static int device_file_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 
 static const struct object_ops device_file_ops =
 {
@@ -546,7 +546,7 @@ static obj_handle_t device_file_flush( struct fd *fd, struct async *async )
     return handle;
 }
 
-static obj_handle_t device_file_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+static int device_file_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     struct device_file *file = get_fd_user( fd );
     struct irp_call *irp;
@@ -561,7 +561,7 @@ static obj_handle_t device_file_ioctl( struct fd *fd, ioctl_code_t code, struct
     irp = create_irp( file, &params, async );
     if (!irp) return 0;
 
-    handle = queue_irp( file, irp, async, 1 );
+    handle = queue_irp( file, irp, async, 0 );
     release_object( irp );
     return handle;
 }
diff --git a/server/fd.c b/server/fd.c
index a073c540fedfcb8266615825d063115278b6b76a..5ab0ff0e21741c8d266d5e0e08bf01d27d6c8101 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -2179,20 +2179,20 @@ obj_handle_t no_fd_flush( struct fd *fd, struct async *async )
 }
 
 /* default ioctl() routine */
-obj_handle_t no_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+int no_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     set_error( STATUS_OBJECT_TYPE_MISMATCH );
     return 0;
 }
 
 /* default ioctl() routine */
-obj_handle_t default_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+int default_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     switch(code)
     {
     case FSCTL_DISMOUNT_VOLUME:
         unmount_device( fd );
-        return 0;
+        return 1;
     default:
         set_error( STATUS_NOT_SUPPORTED );
         return 0;
@@ -2481,19 +2481,14 @@ DECL_HANDLER(ioctl)
     unsigned int access = (req->code >> 14) & (FILE_READ_DATA|FILE_WRITE_DATA);
     struct fd *fd = get_handle_fd_obj( current->process, req->async.handle, access );
     struct async *async;
-    struct iosb *iosb;
 
     if (!fd) return;
 
-    if ((iosb = create_iosb( get_req_data(), get_req_data_size(), get_reply_max_size() )))
+    if ((async = create_request_async( fd, &req->async )))
     {
-        if ((async = create_async( fd, current, &req->async, iosb )))
-        {
-            reply->wait    = fd->fd_ops->ioctl( fd, req->code, async );
-            reply->options = fd->options;
-            release_object( async );
-        }
-        release_object( iosb );
+        reply->wait    = async_handoff( async, fd->fd_ops->ioctl( fd, req->code, async ), NULL );
+        reply->options = fd->options;
+        release_object( async );
     }
     release_object( fd );
 }
diff --git a/server/file.h b/server/file.h
index 1e5ea7a769ec1ac0dd716c15fcbdf0db03afbba8..0dbf8d462080e2984f70a143e3f40ac7147476c8 100644
--- a/server/file.h
+++ b/server/file.h
@@ -58,7 +58,7 @@ struct fd_ops
     /* flush the object buffers */
     obj_handle_t (*flush)(struct fd *, struct async *);
     /* perform an ioctl on the file */
-    obj_handle_t (*ioctl)(struct fd *fd, ioctl_code_t code, struct async *async );
+    int (*ioctl)(struct fd *fd, ioctl_code_t code, struct async *async );
     /* queue an async operation */
     void (*queue_async)(struct fd *, struct async *async, int type, int count);
     /* selected events for async i/o need an update */
@@ -103,8 +103,8 @@ extern void fd_reselect_async( struct fd *fd, struct async_queue *queue );
 extern int no_fd_read( struct fd *fd, struct async *async, file_pos_t pos );
 extern int no_fd_write( struct fd *fd, struct async *async, file_pos_t pos );
 extern obj_handle_t no_fd_flush( struct fd *fd, struct async *async );
-extern obj_handle_t no_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
-extern obj_handle_t default_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+extern int no_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+extern int default_fd_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 extern void no_fd_queue_async( struct fd *fd, struct async *async, int type, int count );
 extern void default_fd_queue_async( struct fd *fd, struct async *async, int type, int count );
 extern void default_fd_reselect_async( struct fd *fd, struct async_queue *queue );
diff --git a/server/named_pipe.c b/server/named_pipe.c
index f33daf816f3b8ba238b40db23f13d1084fd6f5a8..1e51ce8015bd1ca4d113342dd0879325675d7908 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -164,7 +164,7 @@ static void pipe_server_dump( struct object *obj, int verbose );
 static struct fd *pipe_server_get_fd( struct object *obj );
 static void pipe_server_destroy( struct object *obj);
 static obj_handle_t pipe_server_flush( struct fd *fd, struct async *async );
-static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+static int pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 
 static const struct object_ops pipe_server_ops =
 {
@@ -207,7 +207,7 @@ static int pipe_client_signaled( struct object *obj, struct wait_queue_entry *en
 static struct fd *pipe_client_get_fd( struct object *obj );
 static void pipe_client_destroy( struct object *obj );
 static obj_handle_t pipe_client_flush( struct fd *fd, struct async *async );
-static obj_handle_t pipe_client_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+static int pipe_client_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 
 static const struct object_ops pipe_client_ops =
 {
@@ -253,8 +253,7 @@ static struct object *named_pipe_device_open_file( struct object *obj, unsigned
                                                    unsigned int sharing, unsigned int options );
 static void named_pipe_device_destroy( struct object *obj );
 static enum server_fd_type named_pipe_device_get_fd_type( struct fd *fd );
-static obj_handle_t named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code,
-                                             struct async *async );
+static int named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 
 static const struct object_ops named_pipe_device_ops =
 {
@@ -889,7 +888,7 @@ static enum server_fd_type pipe_end_get_fd_type( struct fd *fd )
     return FD_TYPE_PIPE;
 }
 
-static void pipe_end_peek( struct pipe_end *pipe_end )
+static int pipe_end_peek( struct pipe_end *pipe_end )
 {
     unsigned reply_size = get_reply_max_size();
     FILE_PIPE_PEEK_BUFFER *buffer;
@@ -900,13 +899,13 @@ static void pipe_end_peek( struct pipe_end *pipe_end )
     if (!use_server_io( pipe_end ))
     {
         set_error( STATUS_NOT_SUPPORTED );
-        return;
+        return 0;
     }
 
     if (reply_size < offsetof( FILE_PIPE_PEEK_BUFFER, Data ))
     {
         set_error( STATUS_INFO_LENGTH_MISMATCH );
-        return;
+        return 0;
     }
     reply_size -= offsetof( FILE_PIPE_PEEK_BUFFER, Data );
 
@@ -921,18 +920,18 @@ static void pipe_end_peek( struct pipe_end *pipe_end )
     }
     else reply_size = 0;
 
-    if (!(buffer = set_reply_data_size( offsetof( FILE_PIPE_PEEK_BUFFER, Data[reply_size] )))) return;
+    if (!(buffer = set_reply_data_size( offsetof( FILE_PIPE_PEEK_BUFFER, Data[reply_size] )))) return 0;
     buffer->NamedPipeState    = 0;  /* FIXME */
     buffer->ReadDataAvailable = avail;
     buffer->NumberOfMessages  = 0;  /* FIXME */
     buffer->MessageLength     = message_length;
     if (reply_size) memcpy( buffer->Data, (const char *)message->iosb->in_data + message->read_pos, reply_size );
+    return 1;
 }
 
-static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+static int pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     struct pipe_server *server = get_fd_user( fd );
-    obj_handle_t wait_handle = 0;
 
     switch(code)
     {
@@ -943,11 +942,10 @@ static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct
         case ps_wait_connect:
             if (fd_queue_async( server->ioctl_fd, async, ASYNC_TYPE_WAIT ))
             {
-                if (async_is_blocking( async )) wait_handle = alloc_handle( current->process, async, SYNCHRONIZE, 0 );
                 set_server_state( server, ps_wait_open );
                 if (server->pipe->waiters) async_wake_up( server->pipe->waiters, STATUS_SUCCESS );
                 set_error( STATUS_PENDING );
-                return wait_handle;
+                return 1;
             }
             break;
         case ps_connected_server:
@@ -987,31 +985,29 @@ static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct
         case ps_idle_server:
         case ps_wait_open:
             set_error( STATUS_PIPE_LISTENING );
-            break;
+            return 0;
         case ps_wait_connect:
             set_error( STATUS_PIPE_DISCONNECTED );
-            break;
+            return 0;
         }
-        return 0;
+        return 1;
 
     case FSCTL_PIPE_PEEK:
-        pipe_end_peek( &server->pipe_end );
-        return 0;
+        return pipe_end_peek( &server->pipe_end );
 
     default:
         return default_fd_ioctl( fd, code, async );
     }
 }
 
-static obj_handle_t pipe_client_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+static int pipe_client_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     struct pipe_client *client = get_fd_user( fd );
 
     switch(code)
     {
     case FSCTL_PIPE_PEEK:
-        pipe_end_peek( &client->pipe_end );
-        return 0;
+        return pipe_end_peek( &client->pipe_end );
 
     default:
         return default_fd_ioctl( fd, code, async );
@@ -1211,7 +1207,7 @@ static struct object *named_pipe_open_file( struct object *obj, unsigned int acc
     return &client->pipe_end.obj;
 }
 
-static obj_handle_t named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+static int named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     struct named_pipe_device *device = get_fd_user( fd );
 
@@ -1221,7 +1217,6 @@ static obj_handle_t named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code, s
         {
             const FILE_PIPE_WAIT_FOR_BUFFER *buffer = get_req_data();
             data_size_t size = get_req_data_size();
-            obj_handle_t wait_handle = 0;
             struct named_pipe *pipe;
             struct pipe_server *server;
             struct unicode_str name;
@@ -1244,14 +1239,15 @@ static obj_handle_t named_pipe_device_ioctl( struct fd *fd, ioctl_code_t code, s
                 queue_async( pipe->waiters, async );
                 when = buffer->TimeoutSpecified ? buffer->Timeout.QuadPart : pipe->timeout;
                 async_set_timeout( async, when, STATUS_IO_TIMEOUT );
-                if (async_is_blocking( async )) wait_handle = alloc_handle( current->process, async, SYNCHRONIZE, 0 );
+                release_object( pipe );
                 set_error( STATUS_PENDING );
+                return 1;
             }
             else release_object( server );
 
         done:
             release_object( pipe );
-            return wait_handle;
+            return 0;
         }
 
     default:
diff --git a/server/serial.c b/server/serial.c
index fb29288510606169bcc18ccee5d2d1f38c61c55c..396ca22a2cb668cb119e38f966fcd450b7f903ab 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -63,7 +63,7 @@ static struct fd *serial_get_fd( struct object *obj );
 static void serial_destroy(struct object *obj);
 
 static enum server_fd_type serial_get_fd_type( struct fd *fd );
-static obj_handle_t serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+static int serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 static void serial_queue_async( struct fd *fd, struct async *async, int type, int count );
 static void serial_reselect_async( struct fd *fd, struct async_queue *queue );
 
@@ -175,42 +175,49 @@ static enum server_fd_type serial_get_fd_type( struct fd *fd )
     return FD_TYPE_SERIAL;
 }
 
-static obj_handle_t serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+static int serial_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     struct serial *serial = get_fd_user( fd );
 
     switch (code)
     {
     case IOCTL_SERIAL_GET_TIMEOUTS:
-        if (get_reply_max_size() >= sizeof(serial->timeouts))
-            set_reply_data( &serial->timeouts, sizeof(serial->timeouts ));
-        else
+        if (get_reply_max_size() < sizeof(serial->timeouts))
+        {
             set_error( STATUS_BUFFER_TOO_SMALL );
-        return 0;
+            return 0;
+        }
+        set_reply_data( &serial->timeouts, sizeof(serial->timeouts ));
+        return 1;
 
     case IOCTL_SERIAL_SET_TIMEOUTS:
-        if (get_req_data_size() >= sizeof(serial->timeouts))
-            memcpy( &serial->timeouts, get_req_data(), sizeof(serial->timeouts) );
-        else
+        if (get_req_data_size() < sizeof(serial->timeouts))
+        {
             set_error( STATUS_BUFFER_TOO_SMALL );
-        return 0;
+            return 0;
+        }
+        memcpy( &serial->timeouts, get_req_data(), sizeof(serial->timeouts) );
+        return 1;
 
     case IOCTL_SERIAL_GET_WAIT_MASK:
-        if (get_reply_max_size() >= sizeof(serial->eventmask))
-            set_reply_data( &serial->eventmask, sizeof(serial->eventmask) );
-        else
+        if (get_reply_max_size() < sizeof(serial->eventmask))
+        {
             set_error( STATUS_BUFFER_TOO_SMALL );
-        return 0;
+            return 0;
+        }
+        set_reply_data( &serial->eventmask, sizeof(serial->eventmask) );
+        return 1;
 
     case IOCTL_SERIAL_SET_WAIT_MASK:
-        if (get_req_data_size() >= sizeof(serial->eventmask))
+        if (get_req_data_size() < sizeof(serial->eventmask))
         {
-            serial->eventmask = *(unsigned int *)get_req_data();
-            serial->generation++;
-            fd_async_wake_up( serial->fd, ASYNC_TYPE_WAIT, STATUS_SUCCESS );
+            set_error( STATUS_BUFFER_TOO_SMALL );
+            return 0;
         }
-        else set_error( STATUS_BUFFER_TOO_SMALL );
-        return 0;
+        serial->eventmask = *(unsigned int *)get_req_data();
+        serial->generation++;
+        fd_async_wake_up( serial->fd, ASYNC_TYPE_WAIT, STATUS_SUCCESS );
+        return 1;
 
     default:
         set_error( STATUS_NOT_SUPPORTED );
diff --git a/server/sock.c b/server/sock.c
index 1fd625b1a3ae8c6c70a5b502925cd49146e6d3a1..d4af01efaea04ae72f95d57bd9d0e02173fb7d72 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -129,7 +129,7 @@ static void sock_destroy_ifchange_q( struct sock *sock );
 static int sock_get_poll_events( struct fd *fd );
 static void sock_poll_event( struct fd *fd, int event );
 static enum server_fd_type sock_get_fd_type( struct fd *fd );
-static obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
+static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async );
 static void sock_queue_async( struct fd *fd, struct async *async, int type, int count );
 static void sock_reselect_async( struct fd *fd, struct async_queue *queue );
 
@@ -534,10 +534,9 @@ static enum server_fd_type sock_get_fd_type( struct fd *fd )
     return FD_TYPE_SOCKET;
 }
 
-obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
+static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
 {
     struct sock *sock = get_fd_user( fd );
-    obj_handle_t wait_handle = 0;
     struct async_queue *ifchange_q;
 
     assert( sock->obj.ops == &sock_ops );
@@ -552,9 +551,8 @@ obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
         }
         if (!(ifchange_q = sock_get_ifchange_q( sock ))) return 0;
         queue_async( ifchange_q, async );
-        if (async_is_blocking( async )) wait_handle = alloc_handle( current->process, async, SYNCHRONIZE, 0 );
         set_error( STATUS_PENDING );
-        return wait_handle;
+        return 1;
     default:
         set_error( STATUS_NOT_SUPPORTED );
         return 0;