Cleanup: Fix Cycles Apache header.
[blender.git] / intern / cycles / device / device_network.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "device.h"
18 #include "device_intern.h"
19 #include "device_network.h"
20
21 #include "util_foreach.h"
22
23 #if defined(WITH_NETWORK)
24
25 CCL_NAMESPACE_BEGIN
26
27 typedef map<device_ptr, device_ptr> PtrMap;
28 typedef vector<uint8_t> DataVector;
29 typedef map<device_ptr, DataVector> DataMap;
30
31 /* tile list */
32 typedef vector<RenderTile> TileList;
33
34 /* search a list of tiles and find the one that matches the passed render tile */
35 static TileList::iterator tile_list_find(TileList& tile_list, RenderTile& tile)
36 {
37         for(TileList::iterator it = tile_list.begin(); it != tile_list.end(); ++it)
38                 if(tile.x == it->x && tile.y == it->y && tile.start_sample == it->start_sample)
39                         return it;
40         return tile_list.end();
41 }
42
43 class NetworkDevice : public Device
44 {
45 public:
46         boost::asio::io_service io_service;
47         tcp::socket socket;
48         device_ptr mem_counter;
49         DeviceTask the_task; /* todo: handle multiple tasks */
50
51         thread_mutex rpc_lock;
52
53         NetworkDevice(DeviceInfo& info, Stats &stats, const char *address)
54         : Device(info, stats, true), socket(io_service)
55         {
56                 error_func = NetworkError();
57                 stringstream portstr;
58                 portstr << SERVER_PORT;
59
60                 tcp::resolver resolver(io_service);
61                 tcp::resolver::query query(address, portstr.str());
62                 tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
63                 tcp::resolver::iterator end;
64
65                 boost::system::error_code error = boost::asio::error::host_not_found;
66                 while(error && endpoint_iterator != end)
67                 {
68                         socket.close();
69                         socket.connect(*endpoint_iterator++, error);
70                 }
71
72                 if(error)
73                         error_func.network_error(error.message());
74
75                 mem_counter = 0;
76         }
77
78         ~NetworkDevice()
79         {
80                 RPCSend snd(socket, &error_func, "stop");
81                 snd.write();
82         }
83
84         void mem_alloc(device_memory& mem, MemoryType type)
85         {
86                 thread_scoped_lock lock(rpc_lock);
87
88                 mem.device_pointer = ++mem_counter;
89
90                 RPCSend snd(socket, &error_func, "mem_alloc");
91
92                 snd.add(mem);
93                 snd.add(type);
94                 snd.write();
95         }
96
97         void mem_copy_to(device_memory& mem)
98         {
99                 thread_scoped_lock lock(rpc_lock);
100
101                 RPCSend snd(socket, &error_func, "mem_copy_to");
102
103                 snd.add(mem);
104                 snd.write();
105                 snd.write_buffer((void*)mem.data_pointer, mem.memory_size());
106         }
107
108         void mem_copy_from(device_memory& mem, int y, int w, int h, int elem)
109         {
110                 thread_scoped_lock lock(rpc_lock);
111
112                 size_t data_size = mem.memory_size();
113
114                 RPCSend snd(socket, &error_func, "mem_copy_from");
115
116                 snd.add(mem);
117                 snd.add(y);
118                 snd.add(w);
119                 snd.add(h);
120                 snd.add(elem);
121                 snd.write();
122
123                 RPCReceive rcv(socket, &error_func);
124                 rcv.read_buffer((void*)mem.data_pointer, data_size);
125         }
126
127         void mem_zero(device_memory& mem)
128         {
129                 thread_scoped_lock lock(rpc_lock);
130
131                 RPCSend snd(socket, &error_func, "mem_zero");
132
133                 snd.add(mem);
134                 snd.write();
135         }
136
137         void mem_free(device_memory& mem)
138         {
139                 if(mem.device_pointer) {
140                         thread_scoped_lock lock(rpc_lock);
141
142                         RPCSend snd(socket, &error_func, "mem_free");
143
144                         snd.add(mem);
145                         snd.write();
146
147                         mem.device_pointer = 0;
148                 }
149         }
150
151         void const_copy_to(const char *name, void *host, size_t size)
152         {
153                 thread_scoped_lock lock(rpc_lock);
154
155                 RPCSend snd(socket, &error_func, "const_copy_to");
156
157                 string name_string(name);
158
159                 snd.add(name_string);
160                 snd.add(size);
161                 snd.write();
162                 snd.write_buffer(host, size);
163         }
164
165         void tex_alloc(const char *name, device_memory& mem, InterpolationType interpolation, bool periodic)
166         {
167                 thread_scoped_lock lock(rpc_lock);
168
169                 mem.device_pointer = ++mem_counter;
170
171                 RPCSend snd(socket, &error_func, "tex_alloc");
172
173                 string name_string(name);
174
175                 snd.add(name_string);
176                 snd.add(mem);
177                 snd.add(interpolation);
178                 snd.add(periodic);
179                 snd.write();
180                 snd.write_buffer((void*)mem.data_pointer, mem.memory_size());
181         }
182
183         void tex_free(device_memory& mem)
184         {
185                 if(mem.device_pointer) {
186                         thread_scoped_lock lock(rpc_lock);
187
188                         RPCSend snd(socket, &error_func, "tex_free");
189
190                         snd.add(mem);
191                         snd.write();
192
193                         mem.device_pointer = 0;
194                 }
195         }
196
197         bool load_kernels(bool experimental)
198         {
199                 if(error_func.have_error())
200                         return false;
201
202                 thread_scoped_lock lock(rpc_lock);
203
204                 RPCSend snd(socket, &error_func, "load_kernels");
205                 snd.add(experimental);
206                 snd.write();
207
208                 bool result;
209                 RPCReceive rcv(socket, &error_func);
210                 rcv.read(result);
211
212                 return result;
213         }
214
215         void task_add(DeviceTask& task)
216         {
217                 thread_scoped_lock lock(rpc_lock);
218
219                 the_task = task;
220
221                 RPCSend snd(socket, &error_func, "task_add");
222                 snd.add(task);
223                 snd.write();
224         }
225
226         void task_wait()
227         {
228                 thread_scoped_lock lock(rpc_lock);
229
230                 RPCSend snd(socket, &error_func, "task_wait");
231                 snd.write();
232
233                 lock.unlock();
234
235                 TileList the_tiles;
236
237                 /* todo: run this threaded for connecting to multiple clients */
238                 for(;;) {
239                         if(error_func.have_error())
240                                 break;
241
242                         RenderTile tile;
243
244                         lock.lock();
245                         RPCReceive rcv(socket, &error_func);
246
247                         if(rcv.name == "acquire_tile") {
248                                 lock.unlock();
249
250                                 /* todo: watch out for recursive calls! */
251                                 if(the_task.acquire_tile(this, tile)) { /* write return as bool */
252                                         the_tiles.push_back(tile);
253
254                                         lock.lock();
255                                         RPCSend snd(socket, &error_func, "acquire_tile");
256                                         snd.add(tile);
257                                         snd.write();
258                                         lock.unlock();
259                                 }
260                                 else {
261                                         lock.lock();
262                                         RPCSend snd(socket, &error_func, "acquire_tile_none");
263                                         snd.write();
264                                         lock.unlock();
265                                 }
266                         }
267                         else if(rcv.name == "release_tile") {
268                                 rcv.read(tile);
269                                 lock.unlock();
270
271                                 TileList::iterator it = tile_list_find(the_tiles, tile);
272                                 if (it != the_tiles.end()) {
273                                         tile.buffers = it->buffers;
274                                         the_tiles.erase(it);
275                                 }
276
277                                 assert(tile.buffers != NULL);
278
279                                 the_task.release_tile(tile);
280
281                                 lock.lock();
282                                 RPCSend snd(socket, &error_func, "release_tile");
283                                 snd.write();
284                                 lock.unlock();
285                         }
286                         else if(rcv.name == "task_wait_done") {
287                                 lock.unlock();
288                                 break;
289                         }
290                         else
291                                 lock.unlock();
292                 }
293         }
294
295         void task_cancel()
296         {
297                 thread_scoped_lock lock(rpc_lock);
298                 RPCSend snd(socket, &error_func, "task_cancel");
299                 snd.write();
300         }
301
302         int get_split_task_count(DeviceTask& task)
303         {
304                 return 1;
305         }
306
307 private:
308         NetworkError error_func;
309 };
310
311 Device *device_network_create(DeviceInfo& info, Stats &stats, const char *address)
312 {
313         return new NetworkDevice(info, stats, address);
314 }
315
316 void device_network_info(vector<DeviceInfo>& devices)
317 {
318         DeviceInfo info;
319
320         info.type = DEVICE_NETWORK;
321         info.description = "Network Device";
322         info.id = "NETWORK";
323         info.num = 0;
324         info.advanced_shading = true; /* todo: get this info from device */
325         info.pack_images = false;
326
327         devices.push_back(info);
328 }
329
330 class DeviceServer {
331 public:
332         thread_mutex rpc_lock;
333
334         void network_error(const string &message) {
335                 error_func.network_error(message);
336         }
337
338         bool have_error() { return error_func.have_error(); }
339
340         DeviceServer(Device *device_, tcp::socket& socket_)
341         : device(device_), socket(socket_), stop(false), blocked_waiting(false)
342         {
343                 error_func = NetworkError();
344         }
345
346         void listen()
347         {
348                 /* receive remote function calls */
349                 for(;;) {
350                         listen_step();
351
352                         if(stop)
353                                 break;
354                 }
355         }
356
357 protected:
358         void listen_step()
359         {
360                 thread_scoped_lock lock(rpc_lock);
361                 RPCReceive rcv(socket, &error_func);
362
363                 if(rcv.name == "stop")
364                         stop = true;
365                 else
366                         process(rcv, lock);
367         }
368
369         /* create a memory buffer for a device buffer and insert it into mem_data */
370         DataVector &data_vector_insert(device_ptr client_pointer, size_t data_size)
371         {
372                 /* create a new DataVector and insert it into mem_data */
373                 pair<DataMap::iterator,bool> data_ins = mem_data.insert(
374                         DataMap::value_type(client_pointer, DataVector()));
375
376                 /* make sure it was a unique insertion */
377                 assert(data_ins.second);
378
379                 /* get a reference to the inserted vector */
380                 DataVector &data_v = data_ins.first->second;
381
382                 /* size the vector */
383                 data_v.resize(data_size);
384
385                 return data_v;
386         }
387
388         DataVector &data_vector_find(device_ptr client_pointer)
389         {
390                 DataMap::iterator i = mem_data.find(client_pointer);
391                 assert(i != mem_data.end());
392                 return i->second;
393         }
394
395         /* setup mapping and reverse mapping of client_pointer<->real_pointer */
396         void pointer_mapping_insert(device_ptr client_pointer, device_ptr real_pointer)
397         {
398                 pair<PtrMap::iterator,bool> mapins;
399
400                 /* insert mapping from client pointer to our real device pointer */
401                 mapins = ptr_map.insert(PtrMap::value_type(client_pointer, real_pointer));
402                 assert(mapins.second);
403
404                 /* insert reverse mapping from real our device pointer to client pointer */
405                 mapins = ptr_imap.insert(PtrMap::value_type(real_pointer, client_pointer));
406                 assert(mapins.second);
407         }
408
409         device_ptr device_ptr_from_client_pointer(device_ptr client_pointer)
410         {
411                 PtrMap::iterator i = ptr_map.find(client_pointer);
412                 assert(i != ptr_map.end());
413                 return i->second;
414         }
415
416         device_ptr device_ptr_from_client_pointer_erase(device_ptr client_pointer)
417         {
418                 PtrMap::iterator i = ptr_map.find(client_pointer);
419                 assert(i != ptr_map.end());
420
421                 device_ptr result = i->second;
422
423                 /* erase the mapping */
424                 ptr_map.erase(i);
425
426                 /* erase the reverse mapping */
427                 PtrMap::iterator irev = ptr_imap.find(result);
428                 assert(irev != ptr_imap.end());
429                 ptr_imap.erase(irev);
430
431                 /* erase the data vector */
432                 DataMap::iterator idata = mem_data.find(client_pointer);
433                 assert(idata != mem_data.end());
434                 mem_data.erase(idata);
435
436                 return result;
437         }
438
439         /* note that the lock must be already acquired upon entry.
440          * This is necessary because the caller often peeks at
441          * the header and delegates control to here when it doesn't
442          * specifically handle the current RPC.
443          * The lock must be unlocked before returning */
444         void process(RPCReceive& rcv, thread_scoped_lock &lock)
445         {
446                 if(rcv.name == "mem_alloc") {
447                         MemoryType type;
448                         network_device_memory mem;
449                         device_ptr client_pointer;
450
451                         rcv.read(mem);
452                         rcv.read(type);
453
454                         lock.unlock();
455
456                         client_pointer = mem.device_pointer;
457
458                         /* create a memory buffer for the device buffer */
459                         size_t data_size = mem.memory_size();
460                         DataVector &data_v = data_vector_insert(client_pointer, data_size);
461
462                         if(data_size)
463                                 mem.data_pointer = (device_ptr)&(data_v[0]);
464                         else
465                                 mem.data_pointer = 0;
466
467                         /* perform the allocation on the actual device */
468                         device->mem_alloc(mem, type);
469
470                         /* store a mapping to/from client_pointer and real device pointer */
471                         pointer_mapping_insert(client_pointer, mem.device_pointer);
472                 }
473                 else if(rcv.name == "mem_copy_to") {
474                         network_device_memory mem;
475
476                         rcv.read(mem);
477                         lock.unlock();
478
479                         device_ptr client_pointer = mem.device_pointer;
480
481                         DataVector &data_v = data_vector_find(client_pointer);
482
483                         size_t data_size = mem.memory_size();
484
485                         /* get pointer to memory buffer for device buffer */
486                         mem.data_pointer = (device_ptr)&data_v[0];
487
488                         /* copy data from network into memory buffer */
489                         rcv.read_buffer((uint8_t*)mem.data_pointer, data_size);
490
491                         /* translate the client pointer to a real device pointer */
492                         mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
493
494                         /* copy the data from the memory buffer to the device buffer */
495                         device->mem_copy_to(mem);
496                 }
497                 else if(rcv.name == "mem_copy_from") {
498                         network_device_memory mem;
499                         int y, w, h, elem;
500
501                         rcv.read(mem);
502                         rcv.read(y);
503                         rcv.read(w);
504                         rcv.read(h);
505                         rcv.read(elem);
506
507                         device_ptr client_pointer = mem.device_pointer;
508                         mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
509
510                         DataVector &data_v = data_vector_find(client_pointer);
511
512                         mem.data_pointer = (device_ptr)&(data_v[0]);
513
514                         device->mem_copy_from(mem, y, w, h, elem);
515
516                         size_t data_size = mem.memory_size();
517
518                         RPCSend snd(socket, &error_func, "mem_copy_from");
519                         snd.write();
520                         snd.write_buffer((uint8_t*)mem.data_pointer, data_size);
521                         lock.unlock();
522                 }
523                 else if(rcv.name == "mem_zero") {
524                         network_device_memory mem;
525                         
526                         rcv.read(mem);
527                         lock.unlock();
528
529                         device_ptr client_pointer = mem.device_pointer;
530                         mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
531
532                         DataVector &data_v = data_vector_find(client_pointer);
533
534                         mem.data_pointer = (device_ptr)&(data_v[0]);
535
536                         device->mem_zero(mem);
537                 }
538                 else if(rcv.name == "mem_free") {
539                         network_device_memory mem;
540                         device_ptr client_pointer;
541
542                         rcv.read(mem);
543                         lock.unlock();
544
545                         client_pointer = mem.device_pointer;
546
547                         mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer);
548
549                         device->mem_free(mem);
550                 }
551                 else if(rcv.name == "const_copy_to") {
552                         string name_string;
553                         size_t size;
554
555                         rcv.read(name_string);
556                         rcv.read(size);
557
558                         vector<char> host_vector(size);
559                         rcv.read_buffer(&host_vector[0], size);
560                         lock.unlock();
561
562                         device->const_copy_to(name_string.c_str(), &host_vector[0], size);
563                 }
564                 else if(rcv.name == "tex_alloc") {
565                         network_device_memory mem;
566                         string name;
567                         InterpolationType interpolation;
568                         bool periodic;
569                         device_ptr client_pointer;
570
571                         rcv.read(name);
572                         rcv.read(mem);
573                         rcv.read(interpolation);
574                         rcv.read(periodic);
575                         lock.unlock();
576
577                         client_pointer = mem.device_pointer;
578
579                         size_t data_size = mem.memory_size();
580
581                         DataVector &data_v = data_vector_insert(client_pointer, data_size);
582
583                         if(data_size)
584                                 mem.data_pointer = (device_ptr)&(data_v[0]);
585                         else
586                                 mem.data_pointer = 0;
587
588                         rcv.read_buffer((uint8_t*)mem.data_pointer, data_size);
589
590                         device->tex_alloc(name.c_str(), mem, interpolation, periodic);
591
592                         pointer_mapping_insert(client_pointer, mem.device_pointer);
593                 }
594                 else if(rcv.name == "tex_free") {
595                         network_device_memory mem;
596                         device_ptr client_pointer;
597
598                         rcv.read(mem);
599                         lock.unlock();
600
601                         client_pointer = mem.device_pointer;
602
603                         mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer);
604
605                         device->tex_free(mem);
606                 }
607                 else if(rcv.name == "load_kernels") {
608                         bool experimental;
609                         rcv.read(experimental);
610
611                         bool result;
612                         result = device->load_kernels(experimental);
613                         RPCSend snd(socket, &error_func, "load_kernels");
614                         snd.add(result);
615                         snd.write();
616                         lock.unlock();
617                 }
618                 else if(rcv.name == "task_add") {
619                         DeviceTask task;
620
621                         rcv.read(task);
622                         lock.unlock();
623
624                         if(task.buffer)
625                                 task.buffer = device_ptr_from_client_pointer(task.buffer);
626
627                         if(task.rgba_half)
628                                 task.rgba_half = device_ptr_from_client_pointer(task.rgba_half);
629
630                         if(task.rgba_byte)
631                                 task.rgba_byte = device_ptr_from_client_pointer(task.rgba_byte);
632
633                         if(task.shader_input)
634                                 task.shader_input = device_ptr_from_client_pointer(task.shader_input);
635
636                         if(task.shader_output)
637                                 task.shader_output = device_ptr_from_client_pointer(task.shader_output);
638
639
640                         task.acquire_tile = function_bind(&DeviceServer::task_acquire_tile, this, _1, _2);
641                         task.release_tile = function_bind(&DeviceServer::task_release_tile, this, _1);
642                         task.update_progress_sample = function_bind(&DeviceServer::task_update_progress_sample, this);
643                         task.update_tile_sample = function_bind(&DeviceServer::task_update_tile_sample, this, _1);
644                         task.get_cancel = function_bind(&DeviceServer::task_get_cancel, this);
645
646                         device->task_add(task);
647                 }
648                 else if(rcv.name == "task_wait") {
649                         lock.unlock();
650
651                         blocked_waiting = true;
652                         device->task_wait();
653                         blocked_waiting = false;
654
655                         lock.lock();
656                         RPCSend snd(socket, &error_func, "task_wait_done");
657                         snd.write();
658                         lock.unlock();
659                 }
660                 else if(rcv.name == "task_cancel") {
661                         lock.unlock();
662                         device->task_cancel();
663                 }
664                 else if(rcv.name == "acquire_tile") {
665                         AcquireEntry entry;
666                         entry.name = rcv.name;
667                         rcv.read(entry.tile);
668                         acquire_queue.push_back(entry);
669                         lock.unlock();
670                 }
671                 else if(rcv.name == "acquire_tile_none") {
672                         AcquireEntry entry;
673                         entry.name = rcv.name;
674                         acquire_queue.push_back(entry);
675                         lock.unlock();
676                 }
677                 else if(rcv.name == "release_tile") {
678                         AcquireEntry entry;
679                         entry.name = rcv.name;
680                         acquire_queue.push_back(entry);
681                         lock.unlock();
682                 }
683                 else {
684                         cout << "Error: unexpected RPC receive call \"" + rcv.name + "\"\n";
685                         lock.unlock();
686                 }
687         }
688
689         bool task_acquire_tile(Device *device, RenderTile& tile)
690         {
691                 thread_scoped_lock acquire_lock(acquire_mutex);
692
693                 bool result = false;
694
695                 RPCSend snd(socket, &error_func, "acquire_tile");
696                 snd.write();
697
698                 do {
699                         if(blocked_waiting)
700                                 listen_step();
701
702                         /* todo: avoid busy wait loop */
703                         thread_scoped_lock lock(rpc_lock);
704
705                         if(!acquire_queue.empty()) {
706                                 AcquireEntry entry = acquire_queue.front();
707                                 acquire_queue.pop_front();
708
709                                 if(entry.name == "acquire_tile") {
710                                         tile = entry.tile;
711
712                                         if(tile.buffer) tile.buffer = ptr_map[tile.buffer];
713                                         if(tile.rng_state) tile.rng_state = ptr_map[tile.rng_state];
714
715                                         result = true;
716                                         break;
717                                 }
718                                 else if(entry.name == "acquire_tile_none") {
719                                         break;
720                                 }
721                                 else {
722                                         cout << "Error: unexpected acquire RPC receive call \"" + entry.name + "\"\n";
723                                 }
724                         }
725                 } while(acquire_queue.empty() && !stop && !have_error());
726
727                 return result;
728         }
729
730         void task_update_progress_sample()
731         {
732                 ; /* skip */
733         }
734
735         void task_update_tile_sample(RenderTile&)
736         {
737                 ; /* skip */
738         }
739
740         void task_release_tile(RenderTile& tile)
741         {
742                 thread_scoped_lock acquire_lock(acquire_mutex);
743
744                 if(tile.buffer) tile.buffer = ptr_imap[tile.buffer];
745                 if(tile.rng_state) tile.rng_state = ptr_imap[tile.rng_state];
746
747                 {
748                         thread_scoped_lock lock(rpc_lock);
749                         RPCSend snd(socket, &error_func, "release_tile");
750                         snd.add(tile);
751                         snd.write();
752                         lock.unlock();
753                 }
754
755                 do {
756                         if(blocked_waiting)
757                                 listen_step();
758
759                         /* todo: avoid busy wait loop */
760                         thread_scoped_lock lock(rpc_lock);
761
762                         if(!acquire_queue.empty()) {
763                                 AcquireEntry entry = acquire_queue.front();
764                                 acquire_queue.pop_front();
765
766                                 if(entry.name == "release_tile") {
767                                         lock.unlock();
768                                         break;
769                                 }
770                                 else {
771                                         cout << "Error: unexpected release RPC receive call \"" + entry.name + "\"\n";
772                                 }
773                         }
774                 } while(acquire_queue.empty() && !stop);
775         }
776
777         bool task_get_cancel()
778         {
779                 return false;
780         }
781
782         /* properties */
783         Device *device;
784         tcp::socket& socket;
785
786         /* mapping of remote to local pointer */
787         PtrMap ptr_map;
788         PtrMap ptr_imap;
789         DataMap mem_data;
790
791         struct AcquireEntry {
792                 string name;
793                 RenderTile tile;
794         };
795
796         thread_mutex acquire_mutex;
797         list<AcquireEntry> acquire_queue;
798
799         bool stop;
800         bool blocked_waiting;
801 private:
802         NetworkError error_func;
803
804         /* todo: free memory and device (osl) on network error */
805
806 };
807
808 void Device::server_run()
809 {
810         try {
811                 /* starts thread that responds to discovery requests */
812                 ServerDiscovery discovery;
813
814                 for(;;) {
815                         /* accept connection */
816                         boost::asio::io_service io_service;
817                         tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT));
818
819                         tcp::socket socket(io_service);
820                         acceptor.accept(socket);
821
822                         string remote_address = socket.remote_endpoint().address().to_string();
823                         printf("Connected to remote client at: %s\n", remote_address.c_str());
824
825                         DeviceServer server(this, socket);
826                         server.listen();
827
828                         printf("Disconnected.\n");
829                 }
830         }
831         catch(exception& e) {
832                 fprintf(stderr, "Network server exception: %s\n", e.what());
833         }
834 }
835
836 CCL_NAMESPACE_END
837
838 #endif
839
840