tesseract v5.3.3.20231005
svutil.cpp
Go to the documentation of this file.
1
2// 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
62namespace tesseract {
63
64const int kMaxMsgSize = 4096;
65
66// Starts a new process.
67void 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).
164void 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
236static 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
246static 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.
275SVNetwork::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 if (addr_info == nullptr) {
301 // Mark stream_ as invalid.
302 stream_ = -1;
303 } else {
304 stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
305 addr_info->ai_protocol);
306 }
307
308 if (stream_ < 0) {
309 std::cerr << "Failed to open socket" << std::endl;
310 } else if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) {
311 // If server is not there, we will start a new server as local child
312 // process.
313 const char *scrollview_path = getenv("SCROLLVIEW_PATH");
314 if (scrollview_path == nullptr) {
315# ifdef SCROLLVIEW_PATH
316# define _STR(a) # a
317# define _XSTR(a) _STR(a)
318 scrollview_path = _XSTR(SCROLLVIEW_PATH);
319# undef _XSTR
320# undef _STR
321# else
322 scrollview_path = ".";
323# endif
324 }
325 const char *prog = ScrollViewProg();
326 std::string command = ScrollViewCommand(scrollview_path);
327 SVSync::StartProcess(prog, command.c_str());
328
329 // Wait for server to show up.
330 // Note: There is no exception handling in case the server never turns up.
331
332 Close();
333 for (;;) {
334 stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
335 addr_info->ai_protocol);
336 if (stream_ >= 0) {
337 if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) == 0) {
338 break;
339 }
340
341 Close();
342
343 std::cout << "ScrollView: Waiting for server...\n";
344 std::this_thread::sleep_for(std::chrono::seconds(1));
345 }
346 }
347 }
348# ifdef _WIN32
349 // WSACleanup(); // This cause ScrollView windows is not displayed
350# endif // _WIN32
351 freeaddrinfo(addr_info);
352}
353
355 Close();
356 delete[] msg_buffer_in_;
357}
358
359} // namespace tesseract
360
361#endif // !GRAPHICS_DISABLED
#define FALSE
Definition: capi.h:39
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:354
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