All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
svutil.cpp
Go to the documentation of this file.
1 // File: svutil.cpp
3 // Description: ScrollView Utilities
4 // Author: Joern Wanke
5 // Created: Thu Nov 29 2007
6 //
7 // (C) Copyright 2007, Google Inc.
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
19 //
20 // SVUtil contains the SVSync and SVNetwork classes, which are used for
21 // thread/process creation & synchronization and network connection.
22 
23 #include <stdio.h>
24 #ifdef _WIN32
25 struct addrinfo {
26  struct sockaddr* ai_addr;
27  int ai_addrlen;
28  int ai_family;
29  int ai_socktype;
30  int ai_protocol;
31 };
32 #else
33 #include <arpa/inet.h>
34 #include <netinet/in.h>
35 #include <pthread.h>
36 #include <semaphore.h>
37 #include <signal.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <netdb.h>
41 #include <sys/socket.h>
42 #ifdef __linux__
43 #include <sys/prctl.h>
44 #endif
45 #include <unistd.h>
46 #endif
47 
48 #include <cstdlib>
49 #include <cstring>
50 #include <iostream>
51 #include <string>
52 
53 // Include automatically generated configuration file if running autoconf.
54 #ifdef HAVE_CONFIG_H
55 #include "config_auto.h"
56 #endif
57 
58 #ifndef GRAPHICS_DISABLED
59 
60 #include "svutil.h"
61 
62 const int kBufferSize = 65536;
63 const int kMaxMsgSize = 4096;
64 
65 // Signals a thread to exit.
67 #ifdef _WIN32
68  // ExitThread(0);
69 #else
70  pthread_exit(0);
71 #endif
72 }
73 
74 // Starts a new process.
75 void SVSync::StartProcess(const char* executable, const char* args) {
76  std::string proc;
77  proc.append(executable);
78  proc.append(" ");
79  proc.append(args);
80  std::cout << "Starting " << proc << std::endl;
81 #ifdef _WIN32
82  STARTUPINFO start_info;
83  PROCESS_INFORMATION proc_info;
84  GetStartupInfo(&start_info);
85  if (!CreateProcess(NULL, const_cast<char*>(proc.c_str()), NULL, NULL, FALSE,
86  CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL,
87  &start_info, &proc_info))
88  return;
89 #else
90  int pid = fork();
91  if (pid != 0) { // The father process returns
92  } else {
93 #ifdef __linux__
94  // Make sure the java process terminates on exit, since its
95  // broken socket detection seems to be useless.
96  prctl(PR_SET_PDEATHSIG, 2, 0, 0, 0);
97 #endif
98  char* mutable_args = strdup(args);
99  int argc = 1;
100  for (int i = 0; mutable_args[i]; ++i) {
101  if (mutable_args[i] == ' ') {
102  ++argc;
103  }
104  }
105  char** argv = new char*[argc + 2];
106  argv[0] = strdup(executable);
107  argv[1] = mutable_args;
108  argc = 2;
109  bool inquote = false;
110  for (int i = 0; mutable_args[i]; ++i) {
111  if (!inquote && mutable_args[i] == ' ') {
112  mutable_args[i] = '\0';
113  argv[argc++] = mutable_args + i + 1;
114  } else if (mutable_args[i] == '"') {
115  inquote = !inquote;
116  mutable_args[i] = ' ';
117  }
118  }
119  argv[argc] = NULL;
120  execvp(executable, argv);
121  }
122 #endif
123 }
124 
126 #ifdef _WIN32
127  semaphore_ = CreateSemaphore(0, 0, 10, 0);
128 #elif defined(__APPLE__)
129  char name[50];
130  snprintf(name, sizeof(name), "%d", random());
131  sem_unlink(name);
132  semaphore_ = sem_open(name, O_CREAT , S_IWUSR, 0);
133  if (semaphore_ == SEM_FAILED) {
134  perror("sem_open");
135  }
136 #else
137  sem_init(&semaphore_, 0, 0);
138 #endif
139 }
140 
142 #ifdef _WIN32
143  ReleaseSemaphore(semaphore_, 1, NULL);
144 #elif defined(__APPLE__)
145  sem_post(semaphore_);
146 #else
147  sem_post(&semaphore_);
148 #endif
149 }
150 
152 #ifdef _WIN32
153  WaitForSingleObject(semaphore_, INFINITE);
154 #elif defined(__APPLE__)
155  sem_wait(semaphore_);
156 #else
157  sem_wait(&semaphore_);
158 #endif
159 }
160 
162 #ifdef _WIN32
163  mutex_ = CreateMutex(0, FALSE, 0);
164 #else
165  pthread_mutex_init(&mutex_, NULL);
166 #endif
167 }
168 
170 #ifdef _WIN32
171  WaitForSingleObject(mutex_, INFINITE);
172 #else
173  pthread_mutex_lock(&mutex_);
174 #endif
175 }
176 
178 #ifdef _WIN32
179  ReleaseMutex(mutex_);
180 #else
181  pthread_mutex_unlock(&mutex_);
182 #endif
183 }
184 
185 // Create new thread.
186 
187 void SVSync::StartThread(void *(*func)(void*), void* arg) {
188 #ifdef _WIN32
189  LPTHREAD_START_ROUTINE f = (LPTHREAD_START_ROUTINE) func;
190  DWORD threadid;
191  HANDLE newthread = CreateThread(
192  NULL, // default security attributes
193  0, // use default stack size
194  f, // thread function
195  arg, // argument to thread function
196  0, // use default creation flags
197  &threadid); // returns the thread identifier
198 #else
199  pthread_t helper;
200  pthread_create(&helper, NULL, func, arg);
201 #endif
202 }
203 
204 // Place a message in the message buffer (and flush it).
205 void SVNetwork::Send(const char* msg) {
206  mutex_send_->Lock();
207  msg_buffer_out_.append(msg);
208  mutex_send_->Unlock();
209 }
210 
211 // Send the whole buffer.
213  mutex_send_->Lock();
214  while (msg_buffer_out_.size() > 0) {
215  int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0);
216  msg_buffer_out_.erase(0, i);
217  }
218  mutex_send_->Unlock();
219 }
220 
221 // Receive a message from the server.
222 // This will always return one line of char* (denoted by \n).
224  char* result = NULL;
225 #if defined(_WIN32) || defined(__CYGWIN__)
226  if (has_content) { result = strtok (NULL, "\n"); }
227 #else
228  if (buffer_ptr_ != NULL) { result = strtok_r(NULL, "\n", &buffer_ptr_); }
229 #endif
230 
231  // This means there is something left in the buffer and we return it.
232  if (result != NULL) { return result;
233  // Otherwise, we read from the stream_.
234  } else {
235  buffer_ptr_ = NULL;
236  has_content = false;
237 
238  // The timeout length is not really important since we are looping anyway
239  // until a new message is delivered.
240  struct timeval tv;
241  tv.tv_sec = 10;
242  tv.tv_usec = 0;
243 
244  // Set the flags to return when the stream_ is ready to be read.
245  fd_set readfds;
246  FD_ZERO(&readfds);
247  FD_SET(stream_, &readfds);
248 
249  int i = select(stream_+1, &readfds, NULL, NULL, &tv);
250 
251  // The stream_ died.
252  if (i == 0) { return NULL; }
253 
254  // Read the message buffer.
255  i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0);
256 
257  // Server quit (0) or error (-1).
258  if (i <= 0) { return NULL; }
259  msg_buffer_in_[i] = '\0';
260  has_content = true;
261 #ifdef _WIN32
262  return strtok(msg_buffer_in_, "\n");
263 #else
264  // Setup a new string tokenizer.
265  return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_);
266 #endif
267  }
268 }
269 
270 // Close the connection to the server.
272 #ifdef _WIN32
273  closesocket(stream_);
274 #else
275  close(stream_);
276 #endif
277 }
278 
279 
280 // The program to invoke to start ScrollView
281 static const char* ScrollViewProg() {
282 #ifdef _WIN32
283  const char* prog = "java -Xms512m -Xmx1024m";
284 #else
285  const char* prog = "sh";
286 #endif
287  return prog;
288 }
289 
290 
291 // The arguments to the program to invoke to start ScrollView
292 static std::string ScrollViewCommand(std::string scrollview_path) {
293  // The following ugly ifdef is to enable the output of the java runtime
294  // to be sent down a black hole on non-windows to ignore all the
295  // exceptions in piccolo. Ideally piccolo would be debugged to make
296  // this unnecessary.
297  // Also the path has to be separated by ; on windows and : otherwise.
298 #ifdef _WIN32
299  const char* cmd_template = "-Djava.library.path=%s -cp %s/ScrollView.jar;"
300  "%s/piccolo2d-core-3.0.jar:%s/piccolo2d-extras-3.0.jar"
301  " com.google.scrollview.ScrollView";
302 #else
303  const char* cmd_template = "-c \"trap 'kill %%1' 0 1 2 ; java "
304  "-Xms1024m -Xmx2048m -Djava.library.path=%s -cp %s/ScrollView.jar:"
305  "%s/piccolo2d-core-3.0.jar:%s/piccolo2d-extras-3.0.jar"
306  " com.google.scrollview.ScrollView"
307  " & wait\"";
308 #endif
309  int cmdlen = strlen(cmd_template) + 4*strlen(scrollview_path.c_str()) + 1;
310  char* cmd = new char[cmdlen];
311  const char* sv_path = scrollview_path.c_str();
312  snprintf(cmd, cmdlen, cmd_template, sv_path, sv_path, sv_path, sv_path);
313  std::string command(cmd);
314  delete [] cmd;
315  return command;
316 }
317 
318 
319 // Platform-independent freeaddrinfo()
320 static void FreeAddrInfo(struct addrinfo* addr_info) {
321  #if defined(__linux__)
322  freeaddrinfo(addr_info);
323  #else
324  delete addr_info->ai_addr;
325  delete addr_info;
326  #endif
327 }
328 
329 
330 // Non-linux version of getaddrinfo()
331 #if !defined(__linux__)
332 static int GetAddrInfoNonLinux(const char* hostname, int port,
333  struct addrinfo** addr_info) {
334 // Get the host data depending on the OS.
335  struct sockaddr_in* address;
336  *addr_info = new struct addrinfo;
337  memset(*addr_info, 0, sizeof(struct addrinfo));
338  address = new struct sockaddr_in;
339  memset(address, 0, sizeof(struct sockaddr_in));
340 
341  (*addr_info)->ai_addr = (struct sockaddr*) address;
342  (*addr_info)->ai_addrlen = sizeof(struct sockaddr);
343  (*addr_info)->ai_family = AF_INET;
344  (*addr_info)->ai_socktype = SOCK_STREAM;
345 
346  struct hostent *name;
347 #ifdef _WIN32
348  WSADATA wsaData;
349  WSAStartup(MAKEWORD(1, 1), &wsaData);
350  name = gethostbyname(hostname);
351 #else
352  name = gethostbyname(hostname);
353 #endif
354 
355  if (name == NULL) {
356  FreeAddrInfo(*addr_info);
357  *addr_info = NULL;
358  return -1;
359  }
360 
361  // Fill in the appropriate variables to be able to connect to the server.
362  address->sin_family = name->h_addrtype;
363  memcpy((char *) &address->sin_addr.s_addr,
364  name->h_addr_list[0], name->h_length);
365  address->sin_port = htons(port);
366  return 0;
367 }
368 #endif
369 
370 
371 // Platform independent version of getaddrinfo()
372 // Given a hostname:port, produce an addrinfo struct
373 static int GetAddrInfo(const char* hostname, int port,
374  struct addrinfo** address) {
375 #if defined(__linux__)
376  char port_str[40];
377  snprintf(port_str, 40, "%d", port);
378  return getaddrinfo(hostname, port_str, NULL, address);
379 #else
380  return GetAddrInfoNonLinux(hostname, port, address);
381 #endif
382 }
383 
384 
385 // Set up a connection to a ScrollView on hostname:port.
386 SVNetwork::SVNetwork(const char* hostname, int port) {
387  mutex_send_ = new SVMutex();
388  msg_buffer_in_ = new char[kMaxMsgSize + 1];
389  msg_buffer_in_[0] = '\0';
390 
391  has_content = false;
392  buffer_ptr_ = NULL;
393 
394  struct addrinfo *addr_info = NULL;
395 
396  if (GetAddrInfo(hostname, port, &addr_info) != 0) {
397  std::cerr << "Error resolving name for ScrollView host "
398  << std::string(hostname) << ":" << port << std::endl;
399  }
400 
401  stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
402  addr_info->ai_protocol);
403 
404  // If server is not there, we will start a new server as local child process.
405  if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) {
406  const char* scrollview_path = getenv("SCROLLVIEW_PATH");
407  if (scrollview_path == NULL) {
408 #ifdef SCROLLVIEW_PATH
409 #define _STR(a) #a
410 #define _XSTR(a) _STR(a)
411  scrollview_path = _XSTR(SCROLLVIEW_PATH);
412 #undef _XSTR
413 #undef _STR
414 #else
415  scrollview_path = ".";
416 #endif
417  }
418  const char *prog = ScrollViewProg();
419  std::string command = ScrollViewCommand(scrollview_path);
420  SVSync::StartProcess(prog, command.c_str());
421 
422  // Wait for server to show up.
423  // Note: There is no exception handling in case the server never turns up.
424 
425  stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
426  addr_info->ai_protocol);
427 
428  while (connect(stream_, addr_info->ai_addr,
429  addr_info->ai_addrlen) < 0) {
430  std::cout << "ScrollView: Waiting for server...\n";
431 #ifdef _WIN32
432  Sleep(1000);
433 #else
434  sleep(1);
435 #endif
436 
437  stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
438  addr_info->ai_protocol);
439  }
440  }
441  FreeAddrInfo(addr_info);
442 }
443 
445  delete[] msg_buffer_in_;
446  delete mutex_send_;
447 }
448 
449 #endif // GRAPHICS_DISABLED
const int kBufferSize
Definition: svutil.cpp:62
Definition: svutil.h:87
void Lock()
Locks on a mutex.
Definition: svutil.cpp:169
char * strtok_r(char *s1, const char *s2, char **lasts)
Definition: strtok_r.cpp:38
void Close()
Close the connection to the server.
Definition: svutil.cpp:271
void Flush()
Flush the buffer.
Definition: svutil.cpp:212
char * Receive()
Definition: svutil.cpp:223
static void StartProcess(const char *executable, const char *args)
Starts a new process.
Definition: svutil.cpp:75
SVMutex()
Sets up a new mutex.
Definition: svutil.cpp:161
name_table name
SVSemaphore()
Sets up a semaphore.
Definition: svutil.cpp:125
const int kMaxMsgSize
Definition: svutil.cpp:63
void Send(const char *msg)
Put a message in the messagebuffer to the server and try to send it.
Definition: svutil.cpp:205
static void ExitThread()
Signals a thread to exit.
Definition: svutil.cpp:66
void Wait()
Wait on a semaphore.
Definition: svutil.cpp:151
#define FALSE
Definition: capi.h:29
void Signal()
Signal a semaphore.
Definition: svutil.cpp:141
static void StartThread(void *(*func)(void *), void *arg)
Create new thread.
Definition: svutil.cpp:187
~SVNetwork()
Destructor.
Definition: svutil.cpp:444
#define NULL
Definition: host.h:144
void Unlock()
Unlocks on a mutex.
Definition: svutil.cpp:177
SVNetwork(const char *hostname, int port)
Set up a connection to hostname on port.
Definition: svutil.cpp:386