Cycles: Make all #include statements relative to cycles source directory
[blender.git] / intern / cycles / device / device_network.cpp
index 521739b8ef117a0ca9fac5d34822b0b1c115bd6c..66758954f44b4c55783241819482593ec98d2b70 100644 (file)
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-#include "device.h"
-#include "device_intern.h"
-#include "device_network.h"
+#include "device/device.h"
+#include "device/device_intern.h"
+#include "device/device_network.h"
 
-#include "util_foreach.h"
+#include "util/util_foreach.h"
+#include "util/util_logging.h"
+
+#if defined(WITH_NETWORK)
 
 CCL_NAMESPACE_BEGIN
 
-#ifdef WITH_NETWORK
+typedef map<device_ptr, device_ptr> PtrMap;
+typedef vector<uint8_t> DataVector;
+typedef map<device_ptr, DataVector> DataMap;
+
+/* tile list */
+typedef vector<RenderTile> TileList;
+
+/* search a list of tiles and find the one that matches the passed render tile */
+static TileList::iterator tile_list_find(TileList& tile_list, RenderTile& tile)
+{
+       for(TileList::iterator it = tile_list.begin(); it != tile_list.end(); ++it)
+               if(tile.x == it->x && tile.y == it->y && tile.start_sample == it->start_sample)
+                       return it;
+       return tile_list.end();
+}
 
 class NetworkDevice : public Device
 {
@@ -32,9 +49,17 @@ public:
        device_ptr mem_counter;
        DeviceTask the_task; /* todo: handle multiple tasks */
 
-       NetworkDevice(Stats &stats, const char *address)
-       : Device(stats), socket(io_service)
+       thread_mutex rpc_lock;
+
+       virtual bool show_samples() const
+       {
+               return false;
+       }
+
+       NetworkDevice(DeviceInfo& info, Stats &stats, const char *address)
+       : Device(info, stats, true), socket(io_service)
        {
+               error_func = NetworkError();
                stringstream portstr;
                portstr << SERVER_PORT;
 
@@ -51,22 +76,30 @@ public:
                }
 
                if(error)
-                       throw boost::system::system_error(error);
+                       error_func.network_error(error.message());
 
                mem_counter = 0;
        }
 
        ~NetworkDevice()
        {
-               RPCSend snd(socket, "stop");
+               RPCSend snd(socket, &error_func, "stop");
                snd.write();
        }
 
-       void mem_alloc(device_memory& mem, MemoryType type)
+       void mem_alloc(const char *name, device_memory& mem, MemoryType type)
        {
+               if(name) {
+                       VLOG(1) << "Buffer allocate: " << name << ", "
+                                   << string_human_readable_number(mem.memory_size()) << " bytes. ("
+                                   << string_human_readable_size(mem.memory_size()) << ")";
+               }
+
+               thread_scoped_lock lock(rpc_lock);
+
                mem.device_pointer = ++mem_counter;
 
-               RPCSend snd(socket, "mem_alloc");
+               RPCSend snd(socket, &error_func, "mem_alloc");
 
                snd.add(mem);
                snd.add(type);
@@ -75,7 +108,9 @@ public:
 
        void mem_copy_to(device_memory& mem)
        {
-               RPCSend snd(socket, "mem_copy_to");
+               thread_scoped_lock lock(rpc_lock);
+
+               RPCSend snd(socket, &error_func, "mem_copy_to");
 
                snd.add(mem);
                snd.write();
@@ -84,7 +119,11 @@ public:
 
        void mem_copy_from(device_memory& mem, int y, int w, int h, int elem)
        {
-               RPCSend snd(socket, "mem_copy_from");
+               thread_scoped_lock lock(rpc_lock);
+
+               size_t data_size = mem.memory_size();
+
+               RPCSend snd(socket, &error_func, "mem_copy_from");
 
                snd.add(mem);
                snd.add(y);
@@ -93,13 +132,15 @@ public:
                snd.add(elem);
                snd.write();
 
-               RPCReceive rcv(socket);
-               rcv.read_buffer((void*)mem.data_pointer, mem.memory_size());
+               RPCReceive rcv(socket, &error_func);
+               rcv.read_buffer((void*)mem.data_pointer, data_size);
        }
 
        void mem_zero(device_memory& mem)
        {
-               RPCSend snd(socket, "mem_zero");
+               thread_scoped_lock lock(rpc_lock);
+
+               RPCSend snd(socket, &error_func, "mem_zero");
 
                snd.add(mem);
                snd.write();
@@ -108,7 +149,9 @@ public:
        void mem_free(device_memory& mem)
        {
                if(mem.device_pointer) {
-                       RPCSend snd(socket, "mem_free");
+                       thread_scoped_lock lock(rpc_lock);
+
+                       RPCSend snd(socket, &error_func, "mem_free");
 
                        snd.add(mem);
                        snd.write();
@@ -119,7 +162,9 @@ public:
 
        void const_copy_to(const char *name, void *host, size_t size)
        {
-               RPCSend snd(socket, "const_copy_to");
+               thread_scoped_lock lock(rpc_lock);
+
+               RPCSend snd(socket, &error_func, "const_copy_to");
 
                string name_string(name);
 
@@ -129,18 +174,27 @@ public:
                snd.write_buffer(host, size);
        }
 
-       void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
+       void tex_alloc(const char *name,
+                      device_memory& mem,
+                      InterpolationType interpolation,
+                      ExtensionType extension)
        {
+               VLOG(1) << "Texture allocate: " << name << ", "
+                       << string_human_readable_number(mem.memory_size()) << " bytes. ("
+                       << string_human_readable_size(mem.memory_size()) << ")";
+
+               thread_scoped_lock lock(rpc_lock);
+
                mem.device_pointer = ++mem_counter;
 
-               RPCSend snd(socket, "tex_alloc");
+               RPCSend snd(socket, &error_func, "tex_alloc");
 
                string name_string(name);
 
                snd.add(name_string);
                snd.add(mem);
                snd.add(interpolation);
-               snd.add(periodic);
+               snd.add(extension);
                snd.write();
                snd.write_buffer((void*)mem.data_pointer, mem.memory_size());
        }
@@ -148,7 +202,9 @@ public:
        void tex_free(device_memory& mem)
        {
                if(mem.device_pointer) {
-                       RPCSend snd(socket, "tex_free");
+                       thread_scoped_lock lock(rpc_lock);
+
+                       RPCSend snd(socket, &error_func, "tex_free");
 
                        snd.add(mem);
                        snd.write();
@@ -157,74 +213,126 @@ public:
                }
        }
 
+       bool load_kernels(const DeviceRequestedFeatures& requested_features)
+       {
+               if(error_func.have_error())
+                       return false;
+
+               thread_scoped_lock lock(rpc_lock);
+
+               RPCSend snd(socket, &error_func, "load_kernels");
+               snd.add(requested_features.experimental);
+               snd.add(requested_features.max_closure);
+               snd.add(requested_features.max_nodes_group);
+               snd.add(requested_features.nodes_features);
+               snd.write();
+
+               bool result;
+               RPCReceive rcv(socket, &error_func);
+               rcv.read(result);
+
+               return result;
+       }
+
        void task_add(DeviceTask& task)
        {
+               thread_scoped_lock lock(rpc_lock);
+
                the_task = task;
 
-               RPCSend snd(socket, "task_add");
+               RPCSend snd(socket, &error_func, "task_add");
                snd.add(task);
                snd.write();
        }
 
        void task_wait()
        {
-               RPCSend snd(socket, "task_wait");
+               thread_scoped_lock lock(rpc_lock);
+
+               RPCSend snd(socket, &error_func, "task_wait");
                snd.write();
 
-               list<RenderTile> the_tiles;
+               lock.unlock();
+
+               TileList the_tiles;
 
                /* todo: run this threaded for connecting to multiple clients */
                for(;;) {
-                       RPCReceive rcv(socket);
+                       if(error_func.have_error())
+                               break;
+
                        RenderTile tile;
 
+                       lock.lock();
+                       RPCReceive rcv(socket, &error_func);
+
                        if(rcv.name == "acquire_tile") {
+                               lock.unlock();
+
                                /* todo: watch out for recursive calls! */
                                if(the_task.acquire_tile(this, tile)) { /* write return as bool */
                                        the_tiles.push_back(tile);
 
-                                       RPCSend snd(socket, "acquire_tile");
+                                       lock.lock();
+                                       RPCSend snd(socket, &error_func, "acquire_tile");
                                        snd.add(tile);
                                        snd.write();
+                                       lock.unlock();
                                }
                                else {
-                                       RPCSend snd(socket, "acquire_tile_none");
+                                       lock.lock();
+                                       RPCSend snd(socket, &error_func, "acquire_tile_none");
                                        snd.write();
+                                       lock.unlock();
                                }
                        }
                        else if(rcv.name == "release_tile") {
                                rcv.read(tile);
+                               lock.unlock();
 
-                               for(list<RenderTile>::iterator it = the_tiles.begin(); it != the_tiles.end(); it++) {
-                                       if(tile.x == it->x && tile.y == it->y && tile.start_sample == it->start_sample) {
-                                               tile.buffers = it->buffers;
-                                               the_tiles.erase(it);
-                                               break;
-                                       }
+                               TileList::iterator it = tile_list_find(the_tiles, tile);
+                               if(it != the_tiles.end()) {
+                                       tile.buffers = it->buffers;
+                                       the_tiles.erase(it);
                                }
 
                                assert(tile.buffers != NULL);
 
                                the_task.release_tile(tile);
 
-                               RPCSend snd(socket, "release_tile");
+                               lock.lock();
+                               RPCSend snd(socket, &error_func, "release_tile");
                                snd.write();
+                               lock.unlock();
                        }
-                       else if(rcv.name == "task_wait_done")
+                       else if(rcv.name == "task_wait_done") {
+                               lock.unlock();
                                break;
+                       }
+                       else
+                               lock.unlock();
                }
        }
 
        void task_cancel()
        {
-               RPCSend snd(socket, "task_cancel");
+               thread_scoped_lock lock(rpc_lock);
+               RPCSend snd(socket, &error_func, "task_cancel");
                snd.write();
        }
+
+       int get_split_task_count(DeviceTask& task)
+       {
+               return 1;
+       }
+
+private:
+       NetworkError error_func;
 };
 
 Device *device_network_create(DeviceInfo& info, Stats &stats, const char *address)
 {
-       return new NetworkDevice(stats, address);
+       return new NetworkDevice(info, stats, address);
 }
 
 void device_network_info(vector<DeviceInfo>& devices)
@@ -243,65 +351,169 @@ void device_network_info(vector<DeviceInfo>& devices)
 
 class DeviceServer {
 public:
+       thread_mutex rpc_lock;
+
+       void network_error(const string &message) {
+               error_func.network_error(message);
+       }
+
+       bool have_error() { return error_func.have_error(); }
+
        DeviceServer(Device *device_, tcp::socket& socket_)
-       : device(device_), socket(socket_)
+       : device(device_), socket(socket_), stop(false), blocked_waiting(false)
        {
+               error_func = NetworkError();
        }
 
        void listen()
        {
                /* receive remote function calls */
                for(;;) {
-                       RPCReceive rcv(socket);
+                       listen_step();
 
-                       if(rcv.name == "stop")
+                       if(stop)
                                break;
-
-                       process(rcv);
                }
        }
 
 protected:
-       void process(RPCReceive& rcv)
+       void listen_step()
+       {
+               thread_scoped_lock lock(rpc_lock);
+               RPCReceive rcv(socket, &error_func);
+
+               if(rcv.name == "stop")
+                       stop = true;
+               else
+                       process(rcv, lock);
+       }
+
+       /* create a memory buffer for a device buffer and insert it into mem_data */
+       DataVector &data_vector_insert(device_ptr client_pointer, size_t data_size)
        {
-               // fprintf(stderr, "receive process %s\n", rcv.name.c_str());
+               /* create a new DataVector and insert it into mem_data */
+               pair<DataMap::iterator,bool> data_ins = mem_data.insert(
+                       DataMap::value_type(client_pointer, DataVector()));
+
+               /* make sure it was a unique insertion */
+               assert(data_ins.second);
+
+               /* get a reference to the inserted vector */
+               DataVector &data_v = data_ins.first->second;
+
+               /* size the vector */
+               data_v.resize(data_size);
+
+               return data_v;
+       }
+
+       DataVector &data_vector_find(device_ptr client_pointer)
+       {
+               DataMap::iterator i = mem_data.find(client_pointer);
+               assert(i != mem_data.end());
+               return i->second;
+       }
+
+       /* setup mapping and reverse mapping of client_pointer<->real_pointer */
+       void pointer_mapping_insert(device_ptr client_pointer, device_ptr real_pointer)
+       {
+               pair<PtrMap::iterator,bool> mapins;
+
+               /* insert mapping from client pointer to our real device pointer */
+               mapins = ptr_map.insert(PtrMap::value_type(client_pointer, real_pointer));
+               assert(mapins.second);
+
+               /* insert reverse mapping from real our device pointer to client pointer */
+               mapins = ptr_imap.insert(PtrMap::value_type(real_pointer, client_pointer));
+               assert(mapins.second);
+       }
+
+       device_ptr device_ptr_from_client_pointer(device_ptr client_pointer)
+       {
+               PtrMap::iterator i = ptr_map.find(client_pointer);
+               assert(i != ptr_map.end());
+               return i->second;
+       }
 
+       device_ptr device_ptr_from_client_pointer_erase(device_ptr client_pointer)
+       {
+               PtrMap::iterator i = ptr_map.find(client_pointer);
+               assert(i != ptr_map.end());
+
+               device_ptr result = i->second;
+
+               /* erase the mapping */
+               ptr_map.erase(i);
+
+               /* erase the reverse mapping */
+               PtrMap::iterator irev = ptr_imap.find(result);
+               assert(irev != ptr_imap.end());
+               ptr_imap.erase(irev);
+
+               /* erase the data vector */
+               DataMap::iterator idata = mem_data.find(client_pointer);
+               assert(idata != mem_data.end());
+               mem_data.erase(idata);
+
+               return result;
+       }
+
+       /* note that the lock must be already acquired upon entry.
+        * This is necessary because the caller often peeks at
+        * the header and delegates control to here when it doesn't
+        * specifically handle the current RPC.
+        * The lock must be unlocked before returning */
+       void process(RPCReceive& rcv, thread_scoped_lock &lock)
+       {
                if(rcv.name == "mem_alloc") {
                        MemoryType type;
                        network_device_memory mem;
-                       device_ptr remote_pointer;
+                       device_ptr client_pointer;
 
                        rcv.read(mem);
                        rcv.read(type);
 
-                       /* todo: CPU needs mem.data_pointer */
+                       lock.unlock();
+
+                       client_pointer = mem.device_pointer;
 
-                       remote_pointer = mem.device_pointer;
+                       /* create a memory buffer for the device buffer */
+                       size_t data_size = mem.memory_size();
+                       DataVector &data_v = data_vector_insert(client_pointer, data_size);
 
-                       mem_data[remote_pointer] = vector<uint8_t>();
-                       mem_data[remote_pointer].resize(mem.memory_size());
-                       if(mem.memory_size())
-                               mem.data_pointer = (device_ptr)&(mem_data[remote_pointer][0]);
+                       if(data_size)
+                               mem.data_pointer = (device_ptr)&(data_v[0]);
                        else
                                mem.data_pointer = 0;
 
-                       device->mem_alloc(mem, type);
+                       /* perform the allocation on the actual device */
+                       device->mem_alloc(NULL, mem, type);
 
-                       ptr_map[remote_pointer] = mem.device_pointer;
-                       ptr_imap[mem.device_pointer] = remote_pointer;
+                       /* store a mapping to/from client_pointer and real device pointer */
+                       pointer_mapping_insert(client_pointer, mem.device_pointer);
                }
                else if(rcv.name == "mem_copy_to") {
                        network_device_memory mem;
 
                        rcv.read(mem);
+                       lock.unlock();
+
+                       device_ptr client_pointer = mem.device_pointer;
+
+                       DataVector &data_v = data_vector_find(client_pointer);
 
-                       device_ptr remote_pointer = mem.device_pointer;
-                       mem.data_pointer = (device_ptr)&(mem_data[remote_pointer][0]);
+                       size_t data_size = mem.memory_size();
 
-                       rcv.read_buffer((uint8_t*)mem.data_pointer, mem.memory_size());
+                       /* get pointer to memory buffer for device buffer */
+                       mem.data_pointer = (device_ptr)&data_v[0];
 
-                       mem.device_pointer = ptr_map[remote_pointer];
+                       /* copy data from network into memory buffer */
+                       rcv.read_buffer((uint8_t*)mem.data_pointer, data_size);
 
+                       /* translate the client pointer to a real device pointer */
+                       mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
+
+                       /* copy the data from the memory buffer to the device buffer */
                        device->mem_copy_to(mem);
                }
                else if(rcv.name == "mem_copy_from") {
@@ -314,37 +526,47 @@ protected:
                        rcv.read(h);
                        rcv.read(elem);
 
-                       device_ptr remote_pointer = mem.device_pointer;
-                       mem.device_pointer = ptr_map[remote_pointer];
-                       mem.data_pointer = (device_ptr)&(mem_data[remote_pointer][0]);
+                       device_ptr client_pointer = mem.device_pointer;
+                       mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
+
+                       DataVector &data_v = data_vector_find(client_pointer);
+
+                       mem.data_pointer = (device_ptr)&(data_v[0]);
 
                        device->mem_copy_from(mem, y, w, h, elem);
 
-                       RPCSend snd(socket);
+                       size_t data_size = mem.memory_size();
+
+                       RPCSend snd(socket, &error_func, "mem_copy_from");
                        snd.write();
-                       snd.write_buffer((uint8_t*)mem.data_pointer, mem.memory_size());
+                       snd.write_buffer((uint8_t*)mem.data_pointer, data_size);
+                       lock.unlock();
                }
                else if(rcv.name == "mem_zero") {
                        network_device_memory mem;
                        
                        rcv.read(mem);
-                       device_ptr remote_pointer = mem.device_pointer;
-                       mem.device_pointer = ptr_map[mem.device_pointer];
-                       mem.data_pointer = (device_ptr)&(mem_data[remote_pointer][0]);
+                       lock.unlock();
+
+                       device_ptr client_pointer = mem.device_pointer;
+                       mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
+
+                       DataVector &data_v = data_vector_find(client_pointer);
+
+                       mem.data_pointer = (device_ptr)&(data_v[0]);
 
                        device->mem_zero(mem);
                }
                else if(rcv.name == "mem_free") {
                        network_device_memory mem;
-                       device_ptr remote_pointer;
+                       device_ptr client_pointer;
 
                        rcv.read(mem);
+                       lock.unlock();
 
-                       remote_pointer = mem.device_pointer;
-                       mem.device_pointer = ptr_map[mem.device_pointer];
-                       ptr_map.erase(remote_pointer);
-                       ptr_imap.erase(mem.device_pointer);
-                       mem_data.erase(remote_pointer);
+                       client_pointer = mem.device_pointer;
+
+                       mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer);
 
                        device->mem_free(mem);
                }
@@ -357,60 +579,91 @@ protected:
 
                        vector<char> host_vector(size);
                        rcv.read_buffer(&host_vector[0], size);
+                       lock.unlock();
 
                        device->const_copy_to(name_string.c_str(), &host_vector[0], size);
                }
                else if(rcv.name == "tex_alloc") {
                        network_device_memory mem;
                        string name;
-                       bool interpolation;
-                       bool periodic;
-                       device_ptr remote_pointer;
+                       InterpolationType interpolation;
+                       ExtensionType extension_type;
+                       device_ptr client_pointer;
 
                        rcv.read(name);
                        rcv.read(mem);
                        rcv.read(interpolation);
-                       rcv.read(periodic);
+                       rcv.read(extension_type);
+                       lock.unlock();
+
+                       client_pointer = mem.device_pointer;
 
-                       remote_pointer = mem.device_pointer;
+                       size_t data_size = mem.memory_size();
 
-                       mem_data[remote_pointer] = vector<uint8_t>();
-                       mem_data[remote_pointer].resize(mem.memory_size());
-                       if(mem.memory_size())
-                               mem.data_pointer = (device_ptr)&(mem_data[remote_pointer][0]);
+                       DataVector &data_v = data_vector_insert(client_pointer, data_size);
+
+                       if(data_size)
+                               mem.data_pointer = (device_ptr)&(data_v[0]);
                        else
                                mem.data_pointer = 0;
 
-                       rcv.read_buffer((uint8_t*)mem.data_pointer, mem.memory_size());
+                       rcv.read_buffer((uint8_t*)mem.data_pointer, data_size);
 
-                       device->tex_alloc(name.c_str(), mem, interpolation, periodic);
+                       device->tex_alloc(name.c_str(), mem, interpolation, extension_type);
 
-                       ptr_map[remote_pointer] = mem.device_pointer;
-                       ptr_imap[mem.device_pointer] = remote_pointer;
+                       pointer_mapping_insert(client_pointer, mem.device_pointer);
                }
                else if(rcv.name == "tex_free") {
                        network_device_memory mem;
-                       device_ptr remote_pointer;
+                       device_ptr client_pointer;
 
                        rcv.read(mem);
+                       lock.unlock();
+
+                       client_pointer = mem.device_pointer;
 
-                       remote_pointer = mem.device_pointer;
-                       mem.device_pointer = ptr_map[mem.device_pointer];
-                       ptr_map.erase(remote_pointer);
-                       ptr_map.erase(mem.device_pointer);
-                       mem_data.erase(remote_pointer);
+                       mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer);
 
                        device->tex_free(mem);
                }
+               else if(rcv.name == "load_kernels") {
+                       DeviceRequestedFeatures requested_features;
+                       rcv.read(requested_features.experimental);
+                       rcv.read(requested_features.max_closure);
+                       rcv.read(requested_features.max_nodes_group);
+                       rcv.read(requested_features.nodes_features);
+
+                       bool result;
+                       result = device->load_kernels(requested_features);
+                       RPCSend snd(socket, &error_func, "load_kernels");
+                       snd.add(result);
+                       snd.write();
+                       lock.unlock();
+               }
                else if(rcv.name == "task_add") {
                        DeviceTask task;
 
                        rcv.read(task);
+                       lock.unlock();
+
+                       if(task.buffer)
+                               task.buffer = device_ptr_from_client_pointer(task.buffer);
+
+                       if(task.rgba_half)
+                               task.rgba_half = device_ptr_from_client_pointer(task.rgba_half);
+
+                       if(task.rgba_byte)
+                               task.rgba_byte = device_ptr_from_client_pointer(task.rgba_byte);
+
+                       if(task.shader_input)
+                               task.shader_input = device_ptr_from_client_pointer(task.shader_input);
+
+                       if(task.shader_output)
+                               task.shader_output = device_ptr_from_client_pointer(task.shader_output);
+
+                       if(task.shader_output_luma)
+                               task.shader_output_luma = device_ptr_from_client_pointer(task.shader_output_luma);
 
-                       if(task.buffer) task.buffer = ptr_map[task.buffer];
-                       if(task.rgba) task.rgba = ptr_map[task.rgba];
-                       if(task.shader_input) task.shader_input = ptr_map[task.shader_input];
-                       if(task.shader_output) task.shader_output = ptr_map[task.shader_output];
 
                        task.acquire_tile = function_bind(&DeviceServer::task_acquire_tile, this, _1, _2);
                        task.release_tile = function_bind(&DeviceServer::task_release_tile, this, _1);
@@ -421,14 +674,44 @@ protected:
                        device->task_add(task);
                }
                else if(rcv.name == "task_wait") {
+                       lock.unlock();
+
+                       blocked_waiting = true;
                        device->task_wait();
+                       blocked_waiting = false;
 
-                       RPCSend snd(socket, "task_wait_done");
+                       lock.lock();
+                       RPCSend snd(socket, &error_func, "task_wait_done");
                        snd.write();
+                       lock.unlock();
                }
                else if(rcv.name == "task_cancel") {
+                       lock.unlock();
                        device->task_cancel();
                }
+               else if(rcv.name == "acquire_tile") {
+                       AcquireEntry entry;
+                       entry.name = rcv.name;
+                       rcv.read(entry.tile);
+                       acquire_queue.push_back(entry);
+                       lock.unlock();
+               }
+               else if(rcv.name == "acquire_tile_none") {
+                       AcquireEntry entry;
+                       entry.name = rcv.name;
+                       acquire_queue.push_back(entry);
+                       lock.unlock();
+               }
+               else if(rcv.name == "release_tile") {
+                       AcquireEntry entry;
+                       entry.name = rcv.name;
+                       acquire_queue.push_back(entry);
+                       lock.unlock();
+               }
+               else {
+                       cout << "Error: unexpected RPC receive call \"" + rcv.name + "\"\n";
+                       lock.unlock();
+               }
        }
 
        bool task_acquire_tile(Device *device, RenderTile& tile)
@@ -437,27 +720,37 @@ protected:
 
                bool result = false;
 
-               RPCSend snd(socket, "acquire_tile");
+               RPCSend snd(socket, &error_func, "acquire_tile");
                snd.write();
 
-               while(1) {
-                       RPCReceive rcv(socket);
+               do {
+                       if(blocked_waiting)
+                               listen_step();
 
-                       if(rcv.name == "acquire_tile") {
-                               rcv.read(tile);
+                       /* todo: avoid busy wait loop */
+                       thread_scoped_lock lock(rpc_lock);
 
-                               if(tile.buffer) tile.buffer = ptr_map[tile.buffer];
-                               if(tile.rng_state) tile.rng_state = ptr_map[tile.rng_state];
-                               if(tile.rgba) tile.rgba = ptr_map[tile.rgba];
+                       if(!acquire_queue.empty()) {
+                               AcquireEntry entry = acquire_queue.front();
+                               acquire_queue.pop_front();
 
-                               result = true;
-                               break;
+                               if(entry.name == "acquire_tile") {
+                                       tile = entry.tile;
+
+                                       if(tile.buffer) tile.buffer = ptr_map[tile.buffer];
+                                       if(tile.rng_state) tile.rng_state = ptr_map[tile.rng_state];
+
+                                       result = true;
+                                       break;
+                               }
+                               else if(entry.name == "acquire_tile_none") {
+                                       break;
+                               }
+                               else {
+                                       cout << "Error: unexpected acquire RPC receive call \"" + entry.name + "\"\n";
+                               }
                        }
-                       else if(rcv.name == "acquire_tile_none")
-                               break;
-                       else
-                               process(rcv);
-               }
+               } while(acquire_queue.empty() && !stop && !have_error());
 
                return result;
        }
@@ -478,20 +771,35 @@ protected:
 
                if(tile.buffer) tile.buffer = ptr_imap[tile.buffer];
                if(tile.rng_state) tile.rng_state = ptr_imap[tile.rng_state];
-               if(tile.rgba) tile.rgba = ptr_imap[tile.rgba];
 
-               RPCSend snd(socket, "release_tile");
-               snd.add(tile);
-               snd.write();
+               {
+                       thread_scoped_lock lock(rpc_lock);
+                       RPCSend snd(socket, &error_func, "release_tile");
+                       snd.add(tile);
+                       snd.write();
+                       lock.unlock();
+               }
 
-               while(1) {
-                       RPCReceive rcv(socket);
+               do {
+                       if(blocked_waiting)
+                               listen_step();
 
-                       if(rcv.name == "release_tile")
-                               break;
-                       else
-                               process(rcv);
-               }
+                       /* todo: avoid busy wait loop */
+                       thread_scoped_lock lock(rpc_lock);
+
+                       if(!acquire_queue.empty()) {
+                               AcquireEntry entry = acquire_queue.front();
+                               acquire_queue.pop_front();
+
+                               if(entry.name == "release_tile") {
+                                       lock.unlock();
+                                       break;
+                               }
+                               else {
+                                       cout << "Error: unexpected release RPC receive call \"" + entry.name + "\"\n";
+                               }
+                       }
+               } while(acquire_queue.empty() && !stop);
        }
 
        bool task_get_cancel()
@@ -504,13 +812,25 @@ protected:
        tcp::socket& socket;
 
        /* mapping of remote to local pointer */
-       map<device_ptr, device_ptr> ptr_map;
-       map<device_ptr, device_ptr> ptr_imap;
-       map<device_ptr, vector<uint8_t> > mem_data;
+       PtrMap ptr_map;
+       PtrMap ptr_imap;
+       DataMap mem_data;
+
+       struct AcquireEntry {
+               string name;
+               RenderTile tile;
+       };
 
        thread_mutex acquire_mutex;
+       list<AcquireEntry> acquire_queue;
+
+       bool stop;
+       bool blocked_waiting;
+private:
+       NetworkError error_func;
 
        /* todo: free memory and device (osl) on network error */
+
 };
 
 void Device::server_run()
@@ -541,7 +861,8 @@ void Device::server_run()
        }
 }
 
+CCL_NAMESPACE_END
+
 #endif
 
-CCL_NAMESPACE_END