Cycles: Add some statistics logging
[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                 VLOG(1) << "Texture allocate: " << name << ", " << mem.memory_size() << " bytes.";
168
169                 thread_scoped_lock lock(rpc_lock);
170
171                 mem.device_pointer = ++mem_counter;
172
173                 RPCSend snd(socket, &error_func, "tex_alloc");
174
175                 string name_string(name);
176
177                 snd.add(name_string);
178                 snd.add(mem);
179                 snd.add(interpolation);
180                 snd.add(periodic);
181                 snd.write();
182                 snd.write_buffer((void*)mem.data_pointer, mem.memory_size());
183         }
184
185         void tex_free(device_memory& mem)
186         {
187                 if(mem.device_pointer) {
188                         thread_scoped_lock lock(rpc_lock);
189
190                         RPCSend snd(socket, &error_func, "tex_free");
191
192                         snd.add(mem);
193                         snd.write();
194
195                         mem.device_pointer = 0;
196                 }
197         }
198
199         bool load_kernels(bool experimental)
200         {
201                 if(error_func.have_error())
202                         return false;
203
204                 thread_scoped_lock lock(rpc_lock);
205
206                 RPCSend snd(socket, &error_func, "load_kernels");
207                 snd.add(experimental);
208                 snd.write();
209
210                 bool result;
211                 RPCReceive rcv(socket, &error_func);
212                 rcv.read(result);
213
214                 return result;
215         }
216
217         void task_add(DeviceTask& task)
218         {
219                 thread_scoped_lock lock(rpc_lock);
220
221                 the_task = task;
222
223                 RPCSend snd(socket, &error_func, "task_add");
224                 snd.add(task);
225                 snd.write();
226         }
227
228         void task_wait()
229         {
230                 thread_scoped_lock lock(rpc_lock);
231
232                 RPCSend snd(socket, &error_func, "task_wait");
233                 snd.write();
234
235                 lock.unlock();
236
237                 TileList the_tiles;
238
239                 /* todo: run this threaded for connecting to multiple clients */
240                 for(;;) {
241                         if(error_func.have_error())
242                                 break;
243
244                         RenderTile tile;
245
246                         lock.lock();
247                         RPCReceive rcv(socket, &error_func);
248
249                         if(rcv.name == "acquire_tile") {
250                                 lock.unlock();
251
252                                 /* todo: watch out for recursive calls! */
253                                 if(the_task.acquire_tile(this, tile)) { /* write return as bool */
254                                         the_tiles.push_back(tile);
255
256                                         lock.lock();
257                                         RPCSend snd(socket, &error_func, "acquire_tile");
258                                         snd.add(tile);
259                                         snd.write();
260                                         lock.unlock();
261                                 }
262                                 else {
263                                         lock.lock();
264                                         RPCSend snd(socket, &error_func, "acquire_tile_none");
265                                         snd.write();
266                                         lock.unlock();
267                                 }
268                         }
269                         else if(rcv.name == "release_tile") {
270                                 rcv.read(tile);
271                                 lock.unlock();
272
273                                 TileList::iterator it = tile_list_find(the_tiles, tile);
274                                 if(it != the_tiles.end()) {
275                                         tile.buffers = it->buffers;
276                                         the_tiles.erase(it);
277                                 }
278
279                                 assert(tile.buffers != NULL);
280
281                                 the_task.release_tile(tile);
282
283                                 lock.lock();
284                                 RPCSend snd(socket, &error_func, "release_tile");
285                                 snd.write();
286                                 lock.unlock();
287                         }
288                         else if(rcv.name == "task_wait_done") {
289                                 lock.unlock();
290                                 break;
291                         }
292                         else
293                                 lock.unlock();
294                 }
295         }
296
297         void task_cancel()
298         {
299                 thread_scoped_lock lock(rpc_lock);
300                 RPCSend snd(socket, &error_func, "task_cancel");
301                 snd.write();
302         }
303
304         int get_split_task_count(DeviceTask& task)
305         {
306                 return 1;
307         }
308
309 private:
310         NetworkError error_func;
311 };
312
313 Device *device_network_create(DeviceInfo& info, Stats &stats, const char *address)
314 {
315         return new NetworkDevice(info, stats, address);
316 }
317
318 void device_network_info(vector<DeviceInfo>& devices)
319 {
320         DeviceInfo info;
321
322         info.type = DEVICE_NETWORK;
323         info.description = "Network Device";
324         info.id = "NETWORK";
325         info.num = 0;
326         info.advanced_shading = true; /* todo: get this info from device */
327         info.pack_images = false;
328
329         devices.push_back(info);
330 }
331
332 class DeviceServer {
333 public:
334         thread_mutex rpc_lock;
335
336         void network_error(const string &message) {
337                 error_func.network_error(message);
338         }
339
340         bool have_error() { return error_func.have_error(); }
341
342         DeviceServer(Device *device_, tcp::socket& socket_)
343         : device(device_), socket(socket_), stop(false), blocked_waiting(false)
344         {
345                 error_func = NetworkError();
346         }
347
348         void listen()
349         {
350                 /* receive remote function calls */
351                 for(;;) {
352                         listen_step();
353
354                         if(stop)
355                                 break;
356                 }
357         }
358
359 protected:
360         void listen_step()
361         {
362                 thread_scoped_lock lock(rpc_lock);
363                 RPCReceive rcv(socket, &error_func);
364
365                 if(rcv.name == "stop")
366                         stop = true;
367                 else
368                         process(rcv, lock);
369         }
370
371         /* create a memory buffer for a device buffer and insert it into mem_data */
372         DataVector &data_vector_insert(device_ptr client_pointer, size_t data_size)
373         {
374                 /* create a new DataVector and insert it into mem_data */
375                 pair<DataMap::iterator,bool> data_ins = mem_data.insert(
376                         DataMap::value_type(client_pointer, DataVector()));
377
378                 /* make sure it was a unique insertion */
379                 assert(data_ins.second);
380
381                 /* get a reference to the inserted vector */
382                 DataVector &data_v = data_ins.first->second;
383
384                 /* size the vector */
385                 data_v.resize(data_size);
386
387                 return data_v;
388         }
389
390         DataVector &data_vector_find(device_ptr client_pointer)
391         {
392                 DataMap::iterator i = mem_data.find(client_pointer);
393                 assert(i != mem_data.end());
394                 return i->second;
395         }
396
397         /* setup mapping and reverse mapping of client_pointer<->real_pointer */
398         void pointer_mapping_insert(device_ptr client_pointer, device_ptr real_pointer)
399         {
400                 pair<PtrMap::iterator,bool> mapins;
401
402                 /* insert mapping from client pointer to our real device pointer */
403                 mapins = ptr_map.insert(PtrMap::value_type(client_pointer, real_pointer));
404                 assert(mapins.second);
405
406                 /* insert reverse mapping from real our device pointer to client pointer */
407                 mapins = ptr_imap.insert(PtrMap::value_type(real_pointer, client_pointer));
408                 assert(mapins.second);
409         }
410
411         device_ptr device_ptr_from_client_pointer(device_ptr client_pointer)
412         {
413                 PtrMap::iterator i = ptr_map.find(client_pointer);
414                 assert(i != ptr_map.end());
415                 return i->second;
416         }
417
418         device_ptr device_ptr_from_client_pointer_erase(device_ptr client_pointer)
419         {
420                 PtrMap::iterator i = ptr_map.find(client_pointer);
421                 assert(i != ptr_map.end());
422
423                 device_ptr result = i->second;
424
425                 /* erase the mapping */
426                 ptr_map.erase(i);
427
428                 /* erase the reverse mapping */
429                 PtrMap::iterator irev = ptr_imap.find(result);
430                 assert(irev != ptr_imap.end());
431                 ptr_imap.erase(irev);
432
433                 /* erase the data vector */
434                 DataMap::iterator idata = mem_data.find(client_pointer);
435                 assert(idata != mem_data.end());
436                 mem_data.erase(idata);
437
438                 return result;
439         }
440
441         /* note that the lock must be already acquired upon entry.
442          * This is necessary because the caller often peeks at
443          * the header and delegates control to here when it doesn't
444          * specifically handle the current RPC.
445          * The lock must be unlocked before returning */
446         void process(RPCReceive& rcv, thread_scoped_lock &lock)
447         {
448                 if(rcv.name == "mem_alloc") {
449                         MemoryType type;
450                         network_device_memory mem;
451                         device_ptr client_pointer;
452
453                         rcv.read(mem);
454                         rcv.read(type);
455
456                         lock.unlock();
457
458                         client_pointer = mem.device_pointer;
459
460                         /* create a memory buffer for the device buffer */
461                         size_t data_size = mem.memory_size();
462                         DataVector &data_v = data_vector_insert(client_pointer, data_size);
463
464                         if(data_size)
465                                 mem.data_pointer = (device_ptr)&(data_v[0]);
466                         else
467                                 mem.data_pointer = 0;
468
469                         /* perform the allocation on the actual device */
470                         device->mem_alloc(mem, type);
471
472                         /* store a mapping to/from client_pointer and real device pointer */
473                         pointer_mapping_insert(client_pointer, mem.device_pointer);
474                 }
475                 else if(rcv.name == "mem_copy_to") {
476                         network_device_memory mem;
477
478                         rcv.read(mem);
479                         lock.unlock();
480
481                         device_ptr client_pointer = mem.device_pointer;
482
483                         DataVector &data_v = data_vector_find(client_pointer);
484
485                         size_t data_size = mem.memory_size();
486
487                         /* get pointer to memory buffer for device buffer */
488                         mem.data_pointer = (device_ptr)&data_v[0];
489
490                         /* copy data from network into memory buffer */
491                         rcv.read_buffer((uint8_t*)mem.data_pointer, data_size);
492
493                         /* translate the client pointer to a real device pointer */
494                         mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
495
496                         /* copy the data from the memory buffer to the device buffer */
497                         device->mem_copy_to(mem);
498                 }
499                 else if(rcv.name == "mem_copy_from") {
500                         network_device_memory mem;
501                         int y, w, h, elem;
502
503                         rcv.read(mem);
504                         rcv.read(y);
505                         rcv.read(w);
506                         rcv.read(h);
507                         rcv.read(elem);
508
509                         device_ptr client_pointer = mem.device_pointer;
510                         mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
511
512                         DataVector &data_v = data_vector_find(client_pointer);
513
514                         mem.data_pointer = (device_ptr)&(data_v[0]);
515
516                         device->mem_copy_from(mem, y, w, h, elem);
517
518                         size_t data_size = mem.memory_size();
519
520                         RPCSend snd(socket, &error_func, "mem_copy_from");
521                         snd.write();
522                         snd.write_buffer((uint8_t*)mem.data_pointer, data_size);
523                         lock.unlock();
524                 }
525                 else if(rcv.name == "mem_zero") {
526                         network_device_memory mem;
527                         
528                         rcv.read(mem);
529                         lock.unlock();
530
531                         device_ptr client_pointer = mem.device_pointer;
532                         mem.device_pointer = device_ptr_from_client_pointer(client_pointer);
533
534                         DataVector &data_v = data_vector_find(client_pointer);
535
536                         mem.data_pointer = (device_ptr)&(data_v[0]);
537
538                         device->mem_zero(mem);
539                 }
540                 else if(rcv.name == "mem_free") {
541                         network_device_memory mem;
542                         device_ptr client_pointer;
543
544                         rcv.read(mem);
545                         lock.unlock();
546
547                         client_pointer = mem.device_pointer;
548
549                         mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer);
550
551                         device->mem_free(mem);
552                 }
553                 else if(rcv.name == "const_copy_to") {
554                         string name_string;
555                         size_t size;
556
557                         rcv.read(name_string);
558                         rcv.read(size);
559
560                         vector<char> host_vector(size);
561                         rcv.read_buffer(&host_vector[0], size);
562                         lock.unlock();
563
564                         device->const_copy_to(name_string.c_str(), &host_vector[0], size);
565                 }
566                 else if(rcv.name == "tex_alloc") {
567                         network_device_memory mem;
568                         string name;
569                         InterpolationType interpolation;
570                         bool periodic;
571                         device_ptr client_pointer;
572
573                         rcv.read(name);
574                         rcv.read(mem);
575                         rcv.read(interpolation);
576                         rcv.read(periodic);
577                         lock.unlock();
578
579                         client_pointer = mem.device_pointer;
580
581                         size_t data_size = mem.memory_size();
582
583                         DataVector &data_v = data_vector_insert(client_pointer, data_size);
584
585                         if(data_size)
586                                 mem.data_pointer = (device_ptr)&(data_v[0]);
587                         else
588                                 mem.data_pointer = 0;
589
590                         rcv.read_buffer((uint8_t*)mem.data_pointer, data_size);
591
592                         device->tex_alloc(name.c_str(), mem, interpolation, periodic);
593
594                         pointer_mapping_insert(client_pointer, mem.device_pointer);
595                 }
596                 else if(rcv.name == "tex_free") {
597                         network_device_memory mem;
598                         device_ptr client_pointer;
599
600                         rcv.read(mem);
601                         lock.unlock();
602
603                         client_pointer = mem.device_pointer;
604
605                         mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer);
606
607                         device->tex_free(mem);
608                 }
609                 else if(rcv.name == "load_kernels") {
610                         bool experimental;
611                         rcv.read(experimental);
612
613                         bool result;
614                         result = device->load_kernels(experimental);
615                         RPCSend snd(socket, &error_func, "load_kernels");
616                         snd.add(result);
617                         snd.write();
618                         lock.unlock();
619                 }
620                 else if(rcv.name == "task_add") {
621                         DeviceTask task;
622
623                         rcv.read(task);
624                         lock.unlock();
625
626                         if(task.buffer)
627                                 task.buffer = device_ptr_from_client_pointer(task.buffer);
628
629                         if(task.rgba_half)
630                                 task.rgba_half = device_ptr_from_client_pointer(task.rgba_half);
631
632                         if(task.rgba_byte)
633                                 task.rgba_byte = device_ptr_from_client_pointer(task.rgba_byte);
634
635                         if(task.shader_input)
636                                 task.shader_input = device_ptr_from_client_pointer(task.shader_input);
637
638                         if(task.shader_output)
639                                 task.shader_output = device_ptr_from_client_pointer(task.shader_output);
640
641
642                         task.acquire_tile = function_bind(&DeviceServer::task_acquire_tile, this, _1, _2);
643                         task.release_tile = function_bind(&DeviceServer::task_release_tile, this, _1);
644                         task.update_progress_sample = function_bind(&DeviceServer::task_update_progress_sample, this);
645                         task.update_tile_sample = function_bind(&DeviceServer::task_update_tile_sample, this, _1);
646                         task.get_cancel = function_bind(&DeviceServer::task_get_cancel, this);
647
648                         device->task_add(task);
649                 }
650                 else if(rcv.name == "task_wait") {
651                         lock.unlock();
652
653                         blocked_waiting = true;
654                         device->task_wait();
655                         blocked_waiting = false;
656
657                         lock.lock();
658                         RPCSend snd(socket, &error_func, "task_wait_done");
659                         snd.write();
660                         lock.unlock();
661                 }
662                 else if(rcv.name == "task_cancel") {
663                         lock.unlock();
664                         device->task_cancel();
665                 }
666                 else if(rcv.name == "acquire_tile") {
667                         AcquireEntry entry;
668                         entry.name = rcv.name;
669                         rcv.read(entry.tile);
670                         acquire_queue.push_back(entry);
671                         lock.unlock();
672                 }
673                 else if(rcv.name == "acquire_tile_none") {
674                         AcquireEntry entry;
675                         entry.name = rcv.name;
676                         acquire_queue.push_back(entry);
677                         lock.unlock();
678                 }
679                 else if(rcv.name == "release_tile") {
680                         AcquireEntry entry;
681                         entry.name = rcv.name;
682                         acquire_queue.push_back(entry);
683                         lock.unlock();
684                 }
685                 else {
686                         cout << "Error: unexpected RPC receive call \"" + rcv.name + "\"\n";
687                         lock.unlock();
688                 }
689         }
690
691         bool task_acquire_tile(Device *device, RenderTile& tile)
692         {
693                 thread_scoped_lock acquire_lock(acquire_mutex);
694
695                 bool result = false;
696
697                 RPCSend snd(socket, &error_func, "acquire_tile");
698                 snd.write();
699
700                 do {
701                         if(blocked_waiting)
702                                 listen_step();
703
704                         /* todo: avoid busy wait loop */
705                         thread_scoped_lock lock(rpc_lock);
706
707                         if(!acquire_queue.empty()) {
708                                 AcquireEntry entry = acquire_queue.front();
709                                 acquire_queue.pop_front();
710
711                                 if(entry.name == "acquire_tile") {
712                                         tile = entry.tile;
713
714                                         if(tile.buffer) tile.buffer = ptr_map[tile.buffer];
715                                         if(tile.rng_state) tile.rng_state = ptr_map[tile.rng_state];
716
717                                         result = true;
718                                         break;
719                                 }
720                                 else if(entry.name == "acquire_tile_none") {
721                                         break;
722                                 }
723                                 else {
724                                         cout << "Error: unexpected acquire RPC receive call \"" + entry.name + "\"\n";
725                                 }
726                         }
727                 } while(acquire_queue.empty() && !stop && !have_error());
728
729                 return result;
730         }
731
732         void task_update_progress_sample()
733         {
734                 ; /* skip */
735         }
736
737         void task_update_tile_sample(RenderTile&)
738         {
739                 ; /* skip */
740         }
741
742         void task_release_tile(RenderTile& tile)
743         {
744                 thread_scoped_lock acquire_lock(acquire_mutex);
745
746                 if(tile.buffer) tile.buffer = ptr_imap[tile.buffer];
747                 if(tile.rng_state) tile.rng_state = ptr_imap[tile.rng_state];
748
749                 {
750                         thread_scoped_lock lock(rpc_lock);
751                         RPCSend snd(socket, &error_func, "release_tile");
752                         snd.add(tile);
753                         snd.write();
754                         lock.unlock();
755                 }
756
757                 do {
758                         if(blocked_waiting)
759                                 listen_step();
760
761                         /* todo: avoid busy wait loop */
762                         thread_scoped_lock lock(rpc_lock);
763
764                         if(!acquire_queue.empty()) {
765                                 AcquireEntry entry = acquire_queue.front();
766                                 acquire_queue.pop_front();
767
768                                 if(entry.name == "release_tile") {
769                                         lock.unlock();
770                                         break;
771                                 }
772                                 else {
773                                         cout << "Error: unexpected release RPC receive call \"" + entry.name + "\"\n";
774                                 }
775                         }
776                 } while(acquire_queue.empty() && !stop);
777         }
778
779         bool task_get_cancel()
780         {
781                 return false;
782         }
783
784         /* properties */
785         Device *device;
786         tcp::socket& socket;
787
788         /* mapping of remote to local pointer */
789         PtrMap ptr_map;
790         PtrMap ptr_imap;
791         DataMap mem_data;
792
793         struct AcquireEntry {
794                 string name;
795                 RenderTile tile;
796         };
797
798         thread_mutex acquire_mutex;
799         list<AcquireEntry> acquire_queue;
800
801         bool stop;
802         bool blocked_waiting;
803 private:
804         NetworkError error_func;
805
806         /* todo: free memory and device (osl) on network error */
807
808 };
809
810 void Device::server_run()
811 {
812         try {
813                 /* starts thread that responds to discovery requests */
814                 ServerDiscovery discovery;
815
816                 for(;;) {
817                         /* accept connection */
818                         boost::asio::io_service io_service;
819                         tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT));
820
821                         tcp::socket socket(io_service);
822                         acceptor.accept(socket);
823
824                         string remote_address = socket.remote_endpoint().address().to_string();
825                         printf("Connected to remote client at: %s\n", remote_address.c_str());
826
827                         DeviceServer server(this, socket);
828                         server.listen();
829
830                         printf("Disconnected.\n");
831                 }
832         }
833         catch(exception& e) {
834                 fprintf(stderr, "Network server exception: %s\n", e.what());
835         }
836 }
837
838 CCL_NAMESPACE_END
839
840 #endif
841
842