diff options
Diffstat (limited to 'tesseract/src/viewer/svutil.cpp')
-rw-r--r-- | tesseract/src/viewer/svutil.cpp | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/tesseract/src/viewer/svutil.cpp b/tesseract/src/viewer/svutil.cpp new file mode 100644 index 00000000..8d73eb25 --- /dev/null +++ b/tesseract/src/viewer/svutil.cpp @@ -0,0 +1,344 @@ +/////////////////////////////////////////////////////////////////////// +// File: svutil.cpp +// Description: ScrollView Utilities +// Author: Joern Wanke +// +// (C) Copyright 2007, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// +// +// SVUtil contains the SVSync and SVNetwork classes, which are used for +// thread/process creation & synchronization and network connection. + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +# include "config_auto.h" +#endif + +#include "svutil.h" + +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <memory> +#include <string> +#include <thread> // for std::this_thread +#include <vector> + +#ifdef _WIN32 +#pragma comment(lib, "Ws2_32.lib") +# include <winsock2.h> // for fd_set, send, .. +# include <ws2tcpip.h> // for addrinfo +#else +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <semaphore.h> +#include <csignal> +#include <sys/select.h> +#include <sys/socket.h> +#ifdef __linux__ +#include <sys/prctl.h> +#endif +#include <unistd.h> +#endif + +#if defined(_WIN32) && !defined(__GNUC__) +#define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr) +#endif /* _WIN32 && !__GNUC__ */ + +#ifndef GRAPHICS_DISABLED + +namespace tesseract { + +const int kMaxMsgSize = 4096; + +// Starts a new process. +void SVSync::StartProcess(const char* executable, const char* args) { + std::string proc; + proc.append(executable); + proc.append(" "); + proc.append(args); + std::cout << "Starting " << proc << std::endl; +#ifdef _WIN32 + STARTUPINFO start_info; + PROCESS_INFORMATION proc_info; + GetStartupInfo(&start_info); + if (!CreateProcess(nullptr, const_cast<char*>(proc.c_str()), nullptr, nullptr, FALSE, + CREATE_NO_WINDOW | DETACHED_PROCESS, nullptr, nullptr, + &start_info, &proc_info)) + return; +#else + int pid = fork(); + if (pid != 0) { // The father process returns + } else { +#ifdef __linux__ + // Make sure the java process terminates on exit, since its + // broken socket detection seems to be useless. + prctl(PR_SET_PDEATHSIG, 2, 0, 0, 0); +#endif + char* mutable_args = strdup(args); + int argc = 1; + for (int i = 0; mutable_args[i]; ++i) { + if (mutable_args[i] == ' ') { + ++argc; + } + } + std::unique_ptr<char*[]> argv(new char*[argc + 2]); + argv[0] = strdup(executable); + argv[1] = mutable_args; + argc = 2; + bool inquote = false; + for (int i = 0; mutable_args[i]; ++i) { + if (!inquote && mutable_args[i] == ' ') { + mutable_args[i] = '\0'; + argv[argc++] = mutable_args + i + 1; + } else if (mutable_args[i] == '"') { + inquote = !inquote; + mutable_args[i] = ' '; + } + } + argv[argc] = nullptr; + execvp(executable, argv.get()); + free(argv[0]); + free(argv[1]); + } +#endif +} + +SVSemaphore::SVSemaphore() { +#ifdef _WIN32 + semaphore_ = CreateSemaphore(0, 0, 10, 0); +#elif defined(__APPLE__) + char name[50]; + snprintf(name, sizeof(name), "%ld", random()); + sem_unlink(name); + semaphore_ = sem_open(name, O_CREAT , S_IWUSR, 0); + if (semaphore_ == SEM_FAILED) { + perror("sem_open"); + } +#else + sem_init(&semaphore_, 0, 0); +#endif +} + +void SVSemaphore::Signal() { +#ifdef _WIN32 + ReleaseSemaphore(semaphore_, 1, nullptr); +#elif defined(__APPLE__) + sem_post(semaphore_); +#else + sem_post(&semaphore_); +#endif +} + +void SVSemaphore::Wait() { +#ifdef _WIN32 + WaitForSingleObject(semaphore_, INFINITE); +#elif defined(__APPLE__) + sem_wait(semaphore_); +#else + sem_wait(&semaphore_); +#endif +} + +// Place a message in the message buffer (and flush it). +void SVNetwork::Send(const char* msg) { + std::lock_guard<std::mutex> guard(mutex_send_); + msg_buffer_out_.append(msg); +} + +// Send the whole buffer. +void SVNetwork::Flush() { + std::lock_guard<std::mutex> guard(mutex_send_); + while (!msg_buffer_out_.empty()) { + int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0); + msg_buffer_out_.erase(0, i); + } +} + +// Receive a message from the server. +// This will always return one line of char* (denoted by \n). +char* SVNetwork::Receive() { + char* result = nullptr; + if (buffer_ptr_ != nullptr) { + result = strtok_r(nullptr, "\n", &buffer_ptr_); + } + + // This means there is something left in the buffer and we return it. + if (result != nullptr) { return result; + // Otherwise, we read from the stream_. + } else { + buffer_ptr_ = nullptr; + + // The timeout length is not really important since we are looping anyway + // until a new message is delivered. + struct timeval tv; + tv.tv_sec = 10; + tv.tv_usec = 0; + + // Set the flags to return when the stream_ is ready to be read. + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(stream_, &readfds); + + int i = select(stream_+1, &readfds, nullptr, nullptr, &tv); + + // The stream_ died. + if (i == 0) { return nullptr; } + + // Read the message buffer. + i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0); + + // Server quit (0) or error (-1). + if (i <= 0) { return nullptr; } + msg_buffer_in_[i] = '\0'; + // Setup a new string tokenizer. + return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_); + } +} + +// Close the connection to the server. +void SVNetwork::Close() { +#ifdef _WIN32 + closesocket(stream_); +#else + close(stream_); +#endif + // Mark stream_ as invalid. + stream_ = -1; +} + + +// The program to invoke to start ScrollView +static const char* ScrollViewProg() { +#ifdef _WIN32 + const char* prog = "java -Xms512m -Xmx1024m"; +#else + const char* prog = "sh"; +#endif + return prog; +} + + +// The arguments to the program to invoke to start ScrollView +static std::string ScrollViewCommand(std::string scrollview_path) { + // The following ugly ifdef is to enable the output of the java runtime + // to be sent down a black hole on non-windows to ignore all the + // exceptions in piccolo. Ideally piccolo would be debugged to make + // this unnecessary. + // Also the path has to be separated by ; on windows and : otherwise. +#ifdef _WIN32 + const char cmd_template[] = "-Djava.library.path=\"%s\" -jar \"%s/ScrollView.jar\""; + +#else + const char cmd_template[] = + "-c \"trap 'kill %%1' 0 1 2 ; java " + "-Xms1024m -Xmx2048m -jar %s/ScrollView.jar" + " & wait\""; +#endif + size_t cmdlen = sizeof(cmd_template) + 2 * scrollview_path.size() + 1; + std::vector<char> cmd(cmdlen); + const char* sv_path = scrollview_path.c_str(); +#ifdef _WIN32 + snprintf(&cmd[0], cmdlen, cmd_template, sv_path, sv_path); +#else + snprintf(&cmd[0], cmdlen, cmd_template, sv_path); +#endif + std::string command(&cmd[0]); + return command; +} + +// Set up a connection to a ScrollView on hostname:port. +SVNetwork::SVNetwork(const char* hostname, int port) { + msg_buffer_in_ = new char[kMaxMsgSize + 1]; + msg_buffer_in_[0] = '\0'; + + buffer_ptr_ = nullptr; + + struct addrinfo *addr_info = nullptr; + char port_str[40]; + snprintf(port_str, 40, "%d", port); +#ifdef _WIN32 + // Initialize Winsock + WSADATA wsaData; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + std::cerr << "WSAStartup failed: " << iResult << std::endl; + } +#endif // _WIN32 + + if (getaddrinfo(hostname, port_str, nullptr, &addr_info) != 0) { + std::cerr << "Error resolving name for ScrollView host " + << std::string(hostname) << ":" << port << std::endl; +#ifdef _WIN32 + WSACleanup(); +#endif // _WIN32 + } + + stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, + addr_info->ai_protocol); + + if (stream_ < 0) { + std::cerr << "Failed to open socket" << std::endl; + } else if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) { + // If server is not there, we will start a new server as local child process. + const char* scrollview_path = getenv("SCROLLVIEW_PATH"); + if (scrollview_path == nullptr) { +#ifdef SCROLLVIEW_PATH +#define _STR(a) #a +#define _XSTR(a) _STR(a) + scrollview_path = _XSTR(SCROLLVIEW_PATH); +#undef _XSTR +#undef _STR +#else + scrollview_path = "."; +#endif + } + const char *prog = ScrollViewProg(); + std::string command = ScrollViewCommand(scrollview_path); + SVSync::StartProcess(prog, command.c_str()); + + // Wait for server to show up. + // Note: There is no exception handling in case the server never turns up. + + Close(); + for (;;) { + stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, + addr_info->ai_protocol); + if (stream_ >= 0) { + if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) == 0) { + break; + } + + Close(); + + std::cout << "ScrollView: Waiting for server...\n"; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + } +#ifdef _WIN32 + // WSACleanup(); // This cause ScrollView windows is not displayed +#endif // _WIN32 + freeaddrinfo(addr_info); +} + +SVNetwork::~SVNetwork() { + Close(); + delete[] msg_buffer_in_; +} + +} // namespace tesseract + +#endif // !GRAPHICS_DISABLED |