tesseract  5.0.0
svutil.cpp
Go to the documentation of this file.
1 // File: svutil.cpp
3 // Description: ScrollView Utilities
4 // Author: Joern Wanke
5 //
6 // (C) Copyright 2007, Google Inc.
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
18 //
19 // SVUtil contains the SVSync and SVNetwork classes, which are used for
20 // thread/process creation & synchronization and network connection.
21 
22 // Include automatically generated configuration file if running autoconf.
23 #ifdef HAVE_CONFIG_H
24 # include "config_auto.h"
25 #endif
26 
27 #include "svutil.h"
28 
29 #include <cstdio>
30 #include <cstdlib>
31 #include <cstring>
32 #include <iostream>
33 #include <memory>
34 #include <string>
35 #include <thread> // for std::this_thread
36 #include <vector>
37 
38 #ifdef _WIN32
39 # pragma comment(lib, "Ws2_32.lib")
40 # include <winsock2.h> // for fd_set, send, ..
41 # include <ws2tcpip.h> // for addrinfo
42 #else
43 # include <arpa/inet.h>
44 # include <netdb.h>
45 # include <netinet/in.h>
46 # include <semaphore.h>
47 # include <sys/select.h>
48 # include <sys/socket.h>
49 # include <csignal>
50 # ifdef __linux__
51 # include <sys/prctl.h>
52 # endif
53 # include <unistd.h>
54 #endif
55 
56 #if defined(_WIN32) && !defined(__GNUC__)
57 # define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr)
58 #endif /* _WIN32 && !__GNUC__ */
59 
60 #ifndef GRAPHICS_DISABLED
61 
62 namespace tesseract {
63 
64 const int kMaxMsgSize = 4096;
65 
66 // Starts a new process.
67 void SVSync::StartProcess(const char *executable, const char *args) {
68  std::string proc;
69  proc.append(executable);
70  proc.append(" ");
71  proc.append(args);
72  std::cout << "Starting " << proc << std::endl;
73 # ifdef _WIN32
74  STARTUPINFO start_info;
75  PROCESS_INFORMATION proc_info;
76  GetStartupInfo(&start_info);
77  if (!CreateProcess(nullptr, const_cast<char *>(proc.c_str()), nullptr,
78  nullptr, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS,
79  nullptr, nullptr, &start_info, &proc_info))
80  return;
81 # else
82  int pid = fork();
83  if (pid != 0) { // The father process returns
84  } else {
85 # ifdef __linux__
86  // Make sure the java process terminates on exit, since its
87  // broken socket detection seems to be useless.
88  prctl(PR_SET_PDEATHSIG, 2, 0, 0, 0);
89 # endif
90  std::string mutable_args(args);
91  int argc = 1;
92  for (auto ch : mutable_args) {
93  if (ch == ' ') {
94  ++argc;
95  }
96  }
97  std::unique_ptr<char *[]> argv(new char *[argc + 2]);
98  std::string argv0(executable);
99  argv[0] = &argv0[0];
100  argv[1] = &mutable_args[0];
101  argc = 2;
102  bool inquote = false;
103  for (int i = 0; mutable_args[i]; ++i) {
104  if (!inquote && mutable_args[i] == ' ') {
105  mutable_args[i] = '\0';
106  argv[argc++] = &mutable_args[i + 1];
107  } else if (mutable_args[i] == '"') {
108  inquote = !inquote;
109  mutable_args[i] = ' ';
110  }
111  }
112  argv[argc] = nullptr;
113  execvp(executable, argv.get());
114  }
115 # endif
116 }
117 
119 # ifdef _WIN32
120  semaphore_ = CreateSemaphore(0, 0, 10, 0);
121 # elif defined(__APPLE__)
122  auto name = std::to_string(random());
123  sem_unlink(name.c_str());
124  semaphore_ = sem_open(name.c_str(), O_CREAT, S_IWUSR, 0);
125  if (semaphore_ == SEM_FAILED) {
126  perror("sem_open");
127  }
128 # else
129  sem_init(&semaphore_, 0, 0);
130 # endif
131 }
132 
134 # ifdef _WIN32
135  CloseHandle(semaphore_);
136 # elif defined(__APPLE__)
137  sem_close(semaphore_);
138 # else
139  sem_close(&semaphore_);
140 # endif
141 }
142 
144 # ifdef _WIN32
145  ReleaseSemaphore(semaphore_, 1, nullptr);
146 # elif defined(__APPLE__)
147  sem_post(semaphore_);
148 # else
149  sem_post(&semaphore_);
150 # endif
151 }
152 
154 # ifdef _WIN32
155  WaitForSingleObject(semaphore_, INFINITE);
156 # elif defined(__APPLE__)
157  sem_wait(semaphore_);
158 # else
159  sem_wait(&semaphore_);
160 # endif
161 }
162 
163 // Place a message in the message buffer (and flush it).
164 void SVNetwork::Send(const char *msg) {
165  std::lock_guard<std::mutex> guard(mutex_send_);
166  msg_buffer_out_.append(msg);
167 }
168 
169 // Send the whole buffer.
171  std::lock_guard<std::mutex> guard(mutex_send_);
172  while (!msg_buffer_out_.empty()) {
173  int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0);
174  msg_buffer_out_.erase(0, i);
175  }
176 }
177 
178 // Receive a message from the server.
179 // This will always return one line of char* (denoted by \n).
181  char *result = nullptr;
182  if (buffer_ptr_ != nullptr) {
183  result = strtok_r(nullptr, "\n", &buffer_ptr_);
184  }
185 
186  // This means there is something left in the buffer and we return it.
187  if (result != nullptr) {
188  return result;
189  // Otherwise, we read from the stream_.
190  } else {
191  buffer_ptr_ = nullptr;
192 
193  // The timeout length is not really important since we are looping anyway
194  // until a new message is delivered.
195  struct timeval tv;
196  tv.tv_sec = 10;
197  tv.tv_usec = 0;
198 
199  // Set the flags to return when the stream_ is ready to be read.
200  fd_set readfds;
201  FD_ZERO(&readfds);
202  FD_SET(stream_, &readfds);
203 
204  int i = select(stream_ + 1, &readfds, nullptr, nullptr, &tv);
205 
206  // The stream_ died.
207  if (i == 0) {
208  return nullptr;
209  }
210 
211  // Read the message buffer.
212  i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0);
213 
214  // Server quit (0) or error (-1).
215  if (i <= 0) {
216  return nullptr;
217  }
218  msg_buffer_in_[i] = '\0';
219  // Setup a new string tokenizer.
220  return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_);
221  }
222 }
223 
224 // Close the connection to the server.
226 # ifdef _WIN32
227  closesocket(stream_);
228 # else
229  close(stream_);
230 # endif
231  // Mark stream_ as invalid.
232  stream_ = -1;
233 }
234 
235 // The program to invoke to start ScrollView
236 static const char *ScrollViewProg() {
237 # ifdef _WIN32
238  const char *prog = "java -Xms512m -Xmx1024m";
239 # else
240  const char *prog = "sh";
241 # endif
242  return prog;
243 }
244 
245 // The arguments to the program to invoke to start ScrollView
246 static std::string ScrollViewCommand(const std::string &scrollview_path) {
247  // The following ugly ifdef is to enable the output of the java runtime
248  // to be sent down a black hole on non-windows to ignore all the
249  // exceptions in piccolo. Ideally piccolo would be debugged to make
250  // this unnecessary.
251  // Also the path has to be separated by ; on windows and : otherwise.
252 # ifdef _WIN32
253  const char cmd_template[] =
254  "-Djava.library.path=\"%s\" -jar \"%s/ScrollView.jar\"";
255 
256 # else
257  const char cmd_template[] =
258  "-c \"trap 'kill %%1' 0 1 2 ; java "
259  "-Xms1024m -Xmx2048m -jar %s/ScrollView.jar"
260  " & wait\"";
261 # endif
262  size_t cmdlen = sizeof(cmd_template) + 2 * scrollview_path.size() + 1;
263  std::vector<char> cmd(cmdlen);
264  const char *sv_path = scrollview_path.c_str();
265 # ifdef _WIN32
266  snprintf(&cmd[0], cmdlen, cmd_template, sv_path, sv_path);
267 # else
268  snprintf(&cmd[0], cmdlen, cmd_template, sv_path);
269 # endif
270  std::string command(&cmd[0]);
271  return command;
272 }
273 
274 // Set up a connection to a ScrollView on hostname:port.
275 SVNetwork::SVNetwork(const char *hostname, int port) {
276  msg_buffer_in_ = new char[kMaxMsgSize + 1];
277  msg_buffer_in_[0] = '\0';
278 
279  buffer_ptr_ = nullptr;
280 
281  struct addrinfo *addr_info = nullptr;
282  auto port_string = std::to_string(port);
283 # ifdef _WIN32
284  // Initialize Winsock
285  WSADATA wsaData;
286  int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
287  if (iResult != 0) {
288  std::cerr << "WSAStartup failed: " << iResult << std::endl;
289  }
290 # endif // _WIN32
291 
292  if (getaddrinfo(hostname, port_string.c_str(), nullptr, &addr_info) != 0) {
293  std::cerr << "Error resolving name for ScrollView host "
294  << std::string(hostname) << ":" << port << std::endl;
295 # ifdef _WIN32
296  WSACleanup();
297 # endif // _WIN32
298  }
299 
300  stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
301  addr_info->ai_protocol);
302 
303  if (stream_ < 0) {
304  std::cerr << "Failed to open socket" << std::endl;
305  } else if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) {
306  // If server is not there, we will start a new server as local child
307  // process.
308  const char *scrollview_path = getenv("SCROLLVIEW_PATH");
309  if (scrollview_path == nullptr) {
310 # ifdef SCROLLVIEW_PATH
311 # define _STR(a) # a
312 # define _XSTR(a) _STR(a)
313  scrollview_path = _XSTR(SCROLLVIEW_PATH);
314 # undef _XSTR
315 # undef _STR
316 # else
317  scrollview_path = ".";
318 # endif
319  }
320  const char *prog = ScrollViewProg();
321  std::string command = ScrollViewCommand(scrollview_path);
322  SVSync::StartProcess(prog, command.c_str());
323 
324  // Wait for server to show up.
325  // Note: There is no exception handling in case the server never turns up.
326 
327  Close();
328  for (;;) {
329  stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
330  addr_info->ai_protocol);
331  if (stream_ >= 0) {
332  if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) == 0) {
333  break;
334  }
335 
336  Close();
337 
338  std::cout << "ScrollView: Waiting for server...\n";
339  std::this_thread::sleep_for(std::chrono::seconds(1));
340  }
341  }
342  }
343 # ifdef _WIN32
344  // WSACleanup(); // This cause ScrollView windows is not displayed
345 # endif // _WIN32
346  freeaddrinfo(addr_info);
347 }
348 
350  Close();
351  delete[] msg_buffer_in_;
352 }
353 
354 } // namespace tesseract
355 
356 #endif // !GRAPHICS_DISABLED
#define FALSE
Definition: capi.h:41
const int kMaxMsgSize
Definition: scrollview.cpp:46
static void StartProcess(const char *executable, const char *args)
Starts a new process.
Definition: svutil.cpp:67
void Signal()
Signal a semaphore.
Definition: svutil.cpp:143
~SVSemaphore()
Cleans up the mutex.
Definition: svutil.cpp:133
SVSemaphore()
Sets up a semaphore.
Definition: svutil.cpp:118
void Wait()
Wait on a semaphore.
Definition: svutil.cpp:153
void Flush()
Flush the buffer.
Definition: svutil.cpp:170
SVNetwork(const char *hostname, int port)
Set up a connection to hostname on port.
Definition: svutil.cpp:275
~SVNetwork()
Destructor.
Definition: svutil.cpp:349
void Send(const char *msg)
Put a message in the messagebuffer to the server and try to send it.
Definition: svutil.cpp:164
void Close()
Close the connection to the server.
Definition: svutil.cpp:225