[Spice-commits] 5 commits - vdagent/vdagent.cpp vdservice/pci_vdi_port.cpp vdservice/pci_vdi_port.h vdservice/vdi_port.h vdservice/vdservice.cpp vdservice/virtio_vdi_port.cpp vdservice/virtio_vdi_port.h

Arnon Gilboa agilboa at kemper.freedesktop.org
Sun Sep 23 09:33:49 PDT 2012


 vdagent/vdagent.cpp           |  197 +++++++++++++++++++++---------------------
 vdservice/pci_vdi_port.cpp    |    3 
 vdservice/pci_vdi_port.h      |    2 
 vdservice/vdi_port.h          |    2 
 vdservice/vdservice.cpp       |   23 +++-
 vdservice/virtio_vdi_port.cpp |   26 +++--
 vdservice/virtio_vdi_port.h   |    6 -
 7 files changed, 137 insertions(+), 122 deletions(-)

New commits:
commit 4929cb36f20b0a655f509af90ee3a71d5fad34f9
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Sun Sep 16 11:48:09 2012 +0300

    vdservice: stop service on virtio failure
    
    read & write are async, and their completion is handled by handle_event(),
    which returns status used by service execute loop.
    previously an error in GetOverlappedResult caused vdservice hang.
    
    rhbz#839564

diff --git a/vdservice/pci_vdi_port.cpp b/vdservice/pci_vdi_port.cpp
index 4deace1..7466fbc 100644
--- a/vdservice/pci_vdi_port.cpp
+++ b/vdservice/pci_vdi_port.cpp
@@ -124,8 +124,9 @@ int PCIVDIPort::read()
     return n;
 }
 
-void PCIVDIPort::handle_event(int event)
+bool PCIVDIPort::handle_event(int event)
 {
     // do nothing - the event merely serves to wake us up, then we call read/write
     // at VDService::execute start of while(_running) loop.
+    return true;
 }
diff --git a/vdservice/pci_vdi_port.h b/vdservice/pci_vdi_port.h
index caa990f..fcc76dc 100644
--- a/vdservice/pci_vdi_port.h
+++ b/vdservice/pci_vdi_port.h
@@ -41,7 +41,7 @@ public:
     virtual int read();
     virtual unsigned get_num_events() { return PCI_VDI_PORT_EVENT_COUNT; }
     virtual void fill_events(HANDLE* handles);
-    virtual void handle_event(int event);
+    virtual bool handle_event(int event);
 
 private:
     HANDLE _handle;
diff --git a/vdservice/vdi_port.h b/vdservice/vdi_port.h
index 50c4d29..a0fb20e 100644
--- a/vdservice/vdi_port.h
+++ b/vdservice/vdi_port.h
@@ -61,7 +61,7 @@ public:
     virtual bool init() = 0;
     virtual unsigned get_num_events() = 0;
     virtual void fill_events(HANDLE* handles) = 0;
-    virtual void handle_event(int event) = 0;
+    virtual bool handle_event(int event) = 0;
     virtual int write() = 0;
     virtual int read() = 0;
 
diff --git a/vdservice/vdservice.cpp b/vdservice/vdservice.cpp
index b48cbeb..2b925fd 100644
--- a/vdservice/vdservice.cpp
+++ b/vdservice/vdservice.cpp
@@ -130,7 +130,6 @@ private:
     bool _running;
     VDLog* _log;
     unsigned _events_count;
-    unsigned _events_vdi_port_base;
 };
 
 VDService* VDService::_singleton = NULL;
@@ -185,7 +184,6 @@ VDService::VDService()
     , _running (false)
     , _log (NULL)
     , _events_count(0)
-    , _events_vdi_port_base(0)
 {
     ZeroMemory(&_agent_proc_info, sizeof(_agent_proc_info));
     ZeroMemory(&_pipe_state, sizeof(_pipe_state));
@@ -536,13 +534,12 @@ bool VDService::execute()
     vd_printf("created %s", _vdi_port->name());
     _events_count = VD_STATIC_EVENTS_COUNT + _vdi_port->get_num_events() + 1 /*for agent*/;
     _events = new HANDLE[_events_count];
-    _events_vdi_port_base = VD_STATIC_EVENTS_COUNT;
     ZeroMemory(_events, _events_count);
     vd_printf("Connected to server");
     _events[VD_EVENT_PIPE_READ] = _pipe_state.read.overlap.hEvent;
     _events[VD_EVENT_PIPE_WRITE] = _pipe_state.write.overlap.hEvent;
     _events[VD_EVENT_CONTROL] = _control_event;
-    _vdi_port->fill_events(&_events[_events_vdi_port_base]);
+    _vdi_port->fill_events(&_events[VD_STATIC_EVENTS_COUNT]);
     _chunk_size = _chunk_port = 0;
     read_pipe();
     while (_running) {
@@ -602,12 +599,12 @@ bool VDService::execute()
                         }
                     }
                 } else {
-                    if (wait_ret >= WAIT_OBJECT_0 + _events_vdi_port_base &&
-                        wait_ret < WAIT_OBJECT_0 +
-                                   _events_vdi_port_base + _vdi_port->get_num_events()) {
-                        _vdi_port->handle_event(wait_ret - VD_STATIC_EVENTS_COUNT - WAIT_OBJECT_0);
+                    int vdi_event = wait_ret - VD_STATIC_EVENTS_COUNT - WAIT_OBJECT_0;
+                    if (vdi_event >= 0 && vdi_event < _vdi_port->get_num_events()) {
+                        _running = _vdi_port->handle_event(vdi_event);
                     } else {
                         vd_printf("WaitForMultipleObjects failed %lu", GetLastError());
+                        _running = false;
                     }
                 }
             }
diff --git a/vdservice/virtio_vdi_port.cpp b/vdservice/virtio_vdi_port.cpp
index 92eb129..be5568a 100644
--- a/vdservice/virtio_vdi_port.cpp
+++ b/vdservice/virtio_vdi_port.cpp
@@ -51,17 +51,21 @@ void VirtioVDIPort::fill_events(HANDLE* handles) {
     handles[VIRTIO_VDI_PORT_EVENT_READ] = _read.overlap.hEvent;
 }
 
-void VirtioVDIPort::handle_event(int event) {
+bool VirtioVDIPort::handle_event(int event) {
+    bool ret;
+
     switch (event) {
         case VIRTIO_VDI_PORT_EVENT_WRITE:
-            write_completion();
+            ret = write_completion();
             break;
         case VIRTIO_VDI_PORT_EVENT_READ:
-            read_completion();
+            ret = read_completion();
             break;
         default:
             vd_printf("ERROR: unexpected event %d", event);
+            ret = false;
     }
+    return ret;
 }
 
 bool VirtioVDIPort::init()
@@ -113,20 +117,21 @@ int VirtioVDIPort::write()
     return ret;
 }
 
-void VirtioVDIPort::write_completion()
+bool VirtioVDIPort::write_completion()
 {
     DWORD bytes;
 
     if (!_write.pending) {
-        return;
+        return true;
     }
     if (!GetOverlappedResult(_handle, &_write.overlap, &bytes, FALSE)) {
         vd_printf("GetOverlappedResult failed: %lu", GetLastError());
-        return;
+        return false;
     }
     _write.start = _write.ring + (_write.start - _write.ring + bytes) % BUF_SIZE;
     _write.bytes = bytes;
     _write.pending = false;
+    return true;
 }
 
 int VirtioVDIPort::read()
@@ -160,7 +165,7 @@ int VirtioVDIPort::read()
     return ret;
 }
 
-void VirtioVDIPort::read_completion()
+bool VirtioVDIPort::read_completion()
 {
     DWORD bytes;
 
@@ -169,13 +174,14 @@ void VirtioVDIPort::read_completion()
 
         if (err == ERROR_OPERATION_ABORTED || err == ERROR_NO_SYSTEM_RESOURCES) {
             _read.pending = false;
-            return;
+            return true;
         } else if (err != ERROR_MORE_DATA) {
             vd_printf("GetOverlappedResult failed: %lu", err);
-            return;
+            return false;
         }
     }
     _read.end = _read.ring + (_read.end - _read.ring + bytes) % BUF_SIZE;
     _read.bytes = bytes;
     _read.pending = false;
+    return true;
 }
diff --git a/vdservice/virtio_vdi_port.h b/vdservice/virtio_vdi_port.h
index 15b6811..d72edf4 100644
--- a/vdservice/virtio_vdi_port.h
+++ b/vdservice/virtio_vdi_port.h
@@ -17,13 +17,13 @@ public:
     virtual bool init();
     virtual unsigned get_num_events() { return VIRTIO_VDI_PORT_EVENT_COUNT; }
     virtual void fill_events(HANDLE* handles);
-    virtual void handle_event(int event);
+    virtual bool handle_event(int event);
     virtual int write();
     virtual int read();
 
 private:
-    void write_completion();
-    void read_completion();
+    bool write_completion();
+    bool read_completion();
 
 private:
     static VirtioVDIPort* _singleton;
commit 1f56b3812fecf885280213aeb4bdedea7e42dd66
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Thu Sep 13 17:23:02 2012 +0300

    vdservice: retry virtio-serial read on ERROR_NO_SYSTEM_RESOURCES (1450)
    
    as recommended by MS. seems like defined behavior of the driver.
    
    rhbz#839564

diff --git a/vdservice/virtio_vdi_port.cpp b/vdservice/virtio_vdi_port.cpp
index 31a3862..92eb129 100644
--- a/vdservice/virtio_vdi_port.cpp
+++ b/vdservice/virtio_vdi_port.cpp
@@ -167,7 +167,7 @@ void VirtioVDIPort::read_completion()
     if (!GetOverlappedResult(_handle, &_read.overlap, &bytes, FALSE)) {
         DWORD err = GetLastError();
 
-        if (err == ERROR_OPERATION_ABORTED) {
+        if (err == ERROR_OPERATION_ABORTED || err == ERROR_NO_SYSTEM_RESOURCES) {
             _read.pending = false;
             return;
         } else if (err != ERROR_MORE_DATA) {
commit 18f70a3376a1e4a8c0d0a164a21b0be97f4c355a
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Mon Sep 10 10:59:14 2012 +0300

    vdservice: restart vdagent if killed manually
    
    win7-only issue
    
    rhbz#845222

diff --git a/vdservice/vdservice.cpp b/vdservice/vdservice.cpp
index 696f3da..b48cbeb 100644
--- a/vdservice/vdservice.cpp
+++ b/vdservice/vdservice.cpp
@@ -590,6 +590,16 @@ bool VDService::execute()
                         restart_agent(false);
                     } else if (_system_version == SYS_VER_WIN_7_CLASS) {
                         kill_agent();
+                        // Assume agent was killed due to console disconnect, and wait for agent
+                        // normal restart due to console connect. If the agent is not alive yet,
+                        // it was killed manually (or crashed), so let's restart it.
+                        if (WaitForSingleObject(_control_event, VD_AGENT_RESTART_INTERVAL) ==
+                                WAIT_OBJECT_0) {
+                            handle_control_event();
+                        }
+                        if (_running && !_agent_alive) {
+                            restart_agent(false);
+                        }
                     }
                 } else {
                     if (wait_ret >= WAIT_OBJECT_0 + _events_vdi_port_base &&
commit 900ef2b4db2cae9a7ee9f159af6d7bc602005be9
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Mon Sep 10 10:17:23 2012 +0300

    vdagent: don't stop due to UIPI blocking
    
    User Interface Privilege Isolation is usually used only for specific windows of
    system security applications (anti-viruses etc.), so with this patch mouse will
    be irresponsive for these windows but keep working for the rest. A complete
    solution might be switching to server mouse mode while the agent is still active.

diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index 3ffafe3..078e50f 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -429,9 +429,14 @@ bool VDAgent::send_input()
             return false;
         }
     }
-    if (!SendInput(1, &_input, sizeof(INPUT)) && GetLastError() != ERROR_ACCESS_DENIED) {
-        vd_printf("SendInput failed: %lu", GetLastError());
-        ret = _running = false;
+    if (!SendInput(1, &_input, sizeof(INPUT))) {
+        DWORD err = GetLastError();
+        // Don't stop agent due to UIPI blocking, which is usually only for specific windows
+        // of system security applications (anti-viruses etc.)
+        if (err != ERROR_SUCCESS && err != ERROR_ACCESS_DENIED) {
+            vd_printf("SendInput failed: %lu", err);
+            ret = _running = false;
+        }
     }
     _input_time = GetTickCount();
     _desktop_layout->unlock();
commit 4e95b73ecf11000b23cd506fc70a686baf83df5c
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Mon Sep 10 09:48:46 2012 +0300

    vdagent: add message_queue for messages written to pipe
    
    This is only part of the message corruption solution.
    The other part is fixing virtio-serial / spice-qemu-char throttling code.
    
    -replace write_[lock/unlock/completion] calls with [new/enqueue]_message
    -remove clipboard specific _out_msg_* class members
    -remove ugly loop - while (a->_out_msg && a->write_clipboard());
    -add _message_mutex for message queue
    -fix pending_write race using _write_mutex
    -TODO: enqueue large message without dividing it to chunks in advance
    
    rhbz #846427

diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index b8bad44..3ffafe3 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -94,10 +94,10 @@ private:
     enum { CONTROL_STOP, CONTROL_DESKTOP_SWITCH };
     void set_control_event(int control_command);
     void handle_control_event();
-    uint8_t* write_lock(DWORD bytes = 0);
-    void write_unlock(DWORD bytes = 0);
+    VDPipeMessage* new_message(DWORD bytes = 0);
+    void enqueue_message(VDPipeMessage* msg);
     bool write_message(uint32_t type, uint32_t size, void* data);
-    bool write_clipboard();
+    bool write_clipboard(VDAgentMessage* msg, uint32_t size);
     bool connect_pipe();
     bool send_input();
     void set_display_depth(uint32_t depth);
@@ -119,9 +119,6 @@ private:
     HANDLE _clipboard_event;
     VDAgentMessage* _in_msg;
     uint32_t _in_msg_pos;
-    VDAgentMessage* _out_msg;
-    uint32_t _out_msg_pos;
-    uint32_t _out_msg_size;
     bool _pending_input;
     bool _pending_write;
     bool _running;
@@ -131,7 +128,9 @@ private:
     VDPipeState _pipe_state;
     mutex_t _write_mutex;
     mutex_t _control_mutex;
+    mutex_t _message_mutex;
     std::queue<int> _control_queue;
+    std::queue<VDPipeMessage*> _message_queue;
 
     bool _logon_desktop;
     bool _display_setting_initialized;
@@ -167,9 +166,6 @@ VDAgent::VDAgent()
     , _clipboard_event (NULL)
     , _in_msg (NULL)
     , _in_msg_pos (0)
-    , _out_msg (NULL)
-    , _out_msg_pos (0)
-    , _out_msg_size (0)
     , _pending_input (false)
     , _pending_write (false)
     , _running (false)
@@ -193,6 +189,7 @@ VDAgent::VDAgent()
     ZeroMemory(&_pipe_state, sizeof(VDPipeState));
     MUTEX_INIT(_write_mutex);
     MUTEX_INIT(_control_mutex);
+    MUTEX_INIT(_message_mutex);
 
     _singleton = this;
 }
@@ -538,7 +535,7 @@ bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port
     }
 
     DWORD msg_size = VD_MESSAGE_HEADER_SIZE + sizeof(VDAgentReply);
-    reply_pipe_msg = (VDPipeMessage*)write_lock(msg_size);
+    reply_pipe_msg = new_message(msg_size);
     if (!reply_pipe_msg) {
         return false;
     }
@@ -553,10 +550,7 @@ bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port
     reply = (VDAgentReply*)reply_msg->data;
     reply->type = VD_AGENT_MONITORS_CONFIG;
     reply->error = display_count ? VD_AGENT_SUCCESS : VD_AGENT_ERROR;
-    write_unlock(msg_size);
-    if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
-    }
+    enqueue_message(reply_pipe_msg);
     return true;
 }
 
@@ -669,7 +663,7 @@ bool VDAgent::send_announce_capabilities(bool request)
     uint32_t internal_msg_size = sizeof(VDAgentAnnounceCapabilities) + VD_AGENT_CAPS_BYTES;
 
     msg_size = VD_MESSAGE_HEADER_SIZE + internal_msg_size;
-    caps_pipe_msg = (VDPipeMessage*)write_lock(msg_size);
+    caps_pipe_msg = new_message(msg_size);
     if (!caps_pipe_msg) {
         return false;
     }
@@ -694,10 +688,7 @@ bool VDAgent::send_announce_capabilities(bool request)
     for (uint32_t i = 0 ; i < caps_size; ++i) {
         vd_printf("%X", caps->caps[i]);
     }
-    write_unlock(msg_size);
-    if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
-    }
+    enqueue_message(caps_pipe_msg);
     return true;
 }
 
@@ -750,11 +741,10 @@ bool VDAgent::handle_display_config(VDAgentDisplayConfig* display_config, uint32
     }
 
     msg_size = VD_MESSAGE_HEADER_SIZE + sizeof(VDAgentReply);
-    reply_pipe_msg = (VDPipeMessage*)write_lock(msg_size);
+    reply_pipe_msg = new_message(msg_size);
     if (!reply_pipe_msg) {
         return false;
     }
-
     reply_pipe_msg->type = VD_AGENT_COMMAND;
     reply_pipe_msg->opaque = port;
     reply_pipe_msg->size = sizeof(VDAgentMessage) + sizeof(VDAgentReply);
@@ -766,10 +756,7 @@ bool VDAgent::handle_display_config(VDAgentDisplayConfig* display_config, uint32
     reply = (VDAgentReply*)reply_msg->data;
     reply->type = VD_AGENT_DISPLAY_CONFIG;
     reply->error = VD_AGENT_SUCCESS;
-    write_unlock(msg_size);
-    if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
-    }
+    enqueue_message(reply_pipe_msg);
     return true;
 }
 
@@ -778,16 +765,13 @@ bool VDAgent::handle_control(VDPipeMessage* msg)
     switch (msg->type) {
     case VD_AGENT_RESET: {
         vd_printf("Agent reset");
-        VDPipeMessage* ack = (VDPipeMessage*)write_lock(sizeof(VDPipeMessage));
+        VDPipeMessage* ack = new_message(sizeof(VDPipeMessage));
         if (!ack) {
             return false;
         }
         ack->type = VD_AGENT_RESET_ACK;
         ack->opaque = msg->opaque;
-        write_unlock(sizeof(VDPipeMessage));
-        if (!_pending_write) {
-            write_completion(0, 0, &_pipe_state.write.overlap);
-        }
+        enqueue_message(ack);
         break;
     }
     case VD_AGENT_SESSION_LOGON:
@@ -816,30 +800,30 @@ bool VDAgent::handle_control(VDPipeMessage* msg)
 
 //FIXME: division to max size chunks should NOT be here, but in the service
 //       here we should write the max possible size to the pipe
-bool VDAgent::write_clipboard()
+bool VDAgent::write_clipboard(VDAgentMessage* msg, uint32_t size)
 {
-    ASSERT(_out_msg);
-    DWORD n = MIN(sizeof(VDPipeMessage) + _out_msg_size - _out_msg_pos, VD_AGENT_MAX_DATA_SIZE);
-    VDPipeMessage* pipe_msg = (VDPipeMessage*)write_lock(n);
-    if (!pipe_msg) {
-        return false;
-    }
-    pipe_msg->type = VD_AGENT_COMMAND;
-    pipe_msg->opaque = VDP_CLIENT_PORT;
-    pipe_msg->size = n - sizeof(VDPipeMessage);
-    memcpy(pipe_msg->data, (char*)_out_msg + _out_msg_pos, n - sizeof(VDPipeMessage));
-    write_unlock(n);
-    if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
-    }
-    _out_msg_pos += (n - sizeof(VDPipeMessage));
-    if (_out_msg_pos == _out_msg_size) {
-        delete[] (uint8_t *)_out_msg;
-        _out_msg = NULL;
-        _out_msg_size = 0;
-        _out_msg_pos = 0;
-    }
-    return true;
+    uint32_t pos = 0;
+    bool ret = true;
+
+    ASSERT(msg && size);
+    //FIXME: do it smarter - no loop, no memcopy
+    MUTEX_LOCK(_message_mutex);
+    while (pos < size) {
+        DWORD n = MIN(sizeof(VDPipeMessage) + size - pos, VD_AGENT_MAX_DATA_SIZE);
+        VDPipeMessage* pipe_msg = new_message(n);
+        if (!pipe_msg) {
+            ret = false;
+            break;
+        }
+        pipe_msg->type = VD_AGENT_COMMAND;
+        pipe_msg->opaque = VDP_CLIENT_PORT;
+        pipe_msg->size = n - sizeof(VDPipeMessage);
+        memcpy(pipe_msg->data, (char*)msg + pos, n - sizeof(VDPipeMessage));
+        enqueue_message(pipe_msg);
+        pos += (n - sizeof(VDPipeMessage));
+    }
+    MUTEX_UNLOCK(_message_mutex);
+    return ret;
 }
 
 bool VDAgent::write_message(uint32_t type, uint32_t size = 0, void* data = NULL)
@@ -847,7 +831,7 @@ bool VDAgent::write_message(uint32_t type, uint32_t size = 0, void* data = NULL)
     VDPipeMessage* pipe_msg;
     VDAgentMessage* msg;
 
-    pipe_msg = (VDPipeMessage*)write_lock(VD_MESSAGE_HEADER_SIZE + size);
+    pipe_msg = new_message(VD_MESSAGE_HEADER_SIZE + size);
     if (!pipe_msg) {
         return false;
     }
@@ -862,10 +846,7 @@ bool VDAgent::write_message(uint32_t type, uint32_t size = 0, void* data = NULL)
     if (size && data) {
         memcpy(msg->data, data, size);
     }
-    write_unlock(VD_MESSAGE_HEADER_SIZE + size);
-    if (!_pending_write) {
-        write_completion(0, 0, &_pipe_state.write.overlap);
-    }
+    enqueue_message(pipe_msg);
     return true;
 }
 
@@ -993,6 +974,8 @@ bool VDAgent::handle_clipboard_grab(VDAgentClipboardGrab* clipboard_grab, uint32
 // VD_AGENT_CLIPBOARD_NONE and no data, so the client will know the request failed.
 bool VDAgent::handle_clipboard_request(VDAgentClipboardRequest* clipboard_request)
 {
+    VDAgentMessage* msg;
+    uint32_t msg_size;
     UINT format;
     HANDLE clip_data;
     uint8_t* new_data = NULL;
@@ -1008,10 +991,6 @@ bool VDAgent::handle_clipboard_request(VDAgentClipboardRequest* clipboard_reques
         vd_printf("Unsupported clipboard type %u", clipboard_request->type);
         return false;
     }
-    if (_out_msg) {
-        vd_printf("clipboard change is already pending");
-        return false;
-    }
     if (!IsClipboardFormatAvailable(format) || !OpenClipboard(_hwnd)) {
         return false;
     }
@@ -1047,14 +1026,13 @@ bool VDAgent::handle_clipboard_request(VDAgentClipboardRequest* clipboard_reques
         CloseClipboard();
         return false;
     }
-    _out_msg_pos = 0;
-    _out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + new_size;
-    _out_msg = (VDAgentMessage*)new uint8_t[_out_msg_size];
-    _out_msg->protocol = VD_AGENT_PROTOCOL;
-    _out_msg->type = VD_AGENT_CLIPBOARD;
-    _out_msg->opaque = 0;
-    _out_msg->size = (uint32_t)(sizeof(VDAgentClipboard) + new_size);
-    VDAgentClipboard* clipboard = (VDAgentClipboard*)_out_msg->data;
+    msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + new_size;
+    msg = (VDAgentMessage*)new uint8_t[msg_size];
+    msg->protocol = VD_AGENT_PROTOCOL;
+    msg->type = VD_AGENT_CLIPBOARD;
+    msg->opaque = 0;
+    msg->size = (uint32_t)(sizeof(VDAgentClipboard) + new_size);
+    VDAgentClipboard* clipboard = (VDAgentClipboard*)msg->data;
     clipboard->type = clipboard_request->type;
 
     switch (clipboard_request->type) {
@@ -1070,7 +1048,8 @@ bool VDAgent::handle_clipboard_request(VDAgentClipboardRequest* clipboard_reques
         break;
     }
     CloseClipboard();
-    write_clipboard();
+    write_clipboard(msg, msg_size);
+    delete[] (uint8_t *)msg;
     return true;
 }
 
@@ -1281,8 +1260,8 @@ VOID CALLBACK VDAgent::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED ove
 {
     VDAgent* a = _singleton;
     VDPipeState* ps = &a->_pipe_state;
+    DWORD size_left;
 
-    a->_pending_write = false;
     if (!a->_running) {
         return;
     }
@@ -1291,40 +1270,57 @@ VOID CALLBACK VDAgent::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED ove
         a->_running = false;
         return;
     }
-    if (!a->write_lock()) {
-        a->_running = false;
-        return;
-    }
+    MUTEX_LOCK(a->_write_mutex);
     ps->write.start += bytes;
     if (ps->write.start == ps->write.end) {
         ps->write.start = ps->write.end = 0;
-        //DEBUG
-        while (a->_out_msg && a->write_clipboard());
-    } else if (WriteFileEx(ps->pipe, ps->write.data + ps->write.start,
-                           ps->write.end - ps->write.start, overlap, write_completion)) {
-        a->_pending_write = true;
+    }
+
+    MUTEX_LOCK(a->_message_mutex);
+    size_left = sizeof(a->_pipe_state.write.data) - a->_pipe_state.write.end;
+    while (!a->_message_queue.empty()) {
+        VDPipeMessage* msg = a->_message_queue.front();
+        DWORD size = sizeof(VDPipeMessage) + msg->size;
+
+        if (size > size_left) {
+            break;
+        }
+        a->_message_queue.pop();
+        memcpy(a->_pipe_state.write.data + a->_pipe_state.write.end, msg, size);
+        a->_pipe_state.write.end += size;
+        size_left -= size;
+        delete msg;
+    }
+    MUTEX_UNLOCK(a->_message_mutex);
+
+    if (ps->write.start < ps->write.end) {
+        if (WriteFileEx(ps->pipe, ps->write.data + ps->write.start,
+                               ps->write.end - ps->write.start, overlap, write_completion)) {
+            a->_pending_write = true;
+        } else {
+            vd_printf("WriteFileEx() failed: %lu", GetLastError());
+            a->_running = false;
+        }
     } else {
-        vd_printf("WriteFileEx() failed: %lu", GetLastError());
-        a->_running = false;
+        a->_pending_write = false;
     }
-    a->write_unlock();
+    MUTEX_UNLOCK(a->_write_mutex);
 }
 
-uint8_t* VDAgent::write_lock(DWORD bytes)
+VDPipeMessage* VDAgent::new_message(DWORD bytes)
 {
-    MUTEX_LOCK(_write_mutex);
-    if (_pipe_state.write.end + bytes <= sizeof(_pipe_state.write.data)) {
-        return &_pipe_state.write.data[_pipe_state.write.end];
-    } else {
-        MUTEX_UNLOCK(_write_mutex);
-        vd_printf("write buffer is full");
-        return NULL;
-    }
+    return (VDPipeMessage*)(new char[bytes]);
 }
 
-void VDAgent::write_unlock(DWORD bytes)
+void VDAgent::enqueue_message(VDPipeMessage* msg)
 {
-    _pipe_state.write.end += bytes;
+    MUTEX_LOCK(_message_mutex);
+    _message_queue.push(msg);
+    MUTEX_UNLOCK(_message_mutex);
+    MUTEX_LOCK(_write_mutex);
+    if (!_pending_write) {
+        write_completion(0, 0, &_pipe_state.write.overlap);
+    }
     MUTEX_UNLOCK(_write_mutex);
 }
 


More information about the Spice-commits mailing list