blob: 8fba7e9e95b07e7f1d670d9f6aec3863f652d89f [file] [log] [blame]
///////////////////////////////////////////////////////////////////////
// File: svutil.cpp
// Description: ScrollView Utilities
// Author: Joern Wanke
// Created: Thu Nov 29 2007
//
// (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.
#ifndef GRAPHICS_DISABLED
#include "svutil.h"
#ifdef WIN32
#include <windows.h>
#include <winsock.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#ifdef __linux__
#include <sys/prctl.h>
#endif
#endif
#include <stdio.h>
#include <iostream>
const int kBufferSize = 65536;
const int kMaxMsgSize = 4096;
// Signals a thread to exit.
void SVSync::ExitThread() {
#ifdef WIN32
//ExitThread(0);
#else
pthread_exit(0);
#endif
}
// Starts a new process.
void SVSync::StartProcess(const char* executable, const char* args) {
#ifdef WIN32
std::string proc;
proc.append(executable);
proc.append(" ");
proc.append(args);
std::cout << "Starting " << proc << std::endl;
STARTUPINFO start_info;
PROCESS_INFORMATION proc_info;
GetStartupInfo(&start_info);
if (!CreateProcess(NULL, const_cast<char*>(proc.c_str()), NULL, NULL, FALSE,
CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL,
&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;
}
}
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] = NULL;
execvp(executable, argv);
}
#endif
}
SVSemaphore::SVSemaphore() {
#ifdef WIN32
semaphore_ = CreateSemaphore(0, 0, 10, 0);
#else
sem_init(&semaphore_, 0, 0);
#endif
}
void SVSemaphore::Signal() {
#ifdef WIN32
ReleaseSemaphore(semaphore_, 1, NULL);
#else
sem_post(&semaphore_);
#endif
}
void SVSemaphore::Wait() {
#ifdef WIN32
WaitForSingleObject(semaphore_, INFINITE);
#else
sem_wait(&semaphore_);
#endif
}
SVMutex::SVMutex() {
#ifdef WIN32
mutex_ = CreateMutex(0, FALSE, 0);
#else
pthread_mutex_init(&mutex_, NULL);
#endif
}
void SVMutex::Lock() {
#ifdef WIN32
WaitForSingleObject(mutex_, INFINITE);
#else
pthread_mutex_lock(&mutex_);
#endif
}
void SVMutex::Unlock() {
#ifdef WIN32
ReleaseMutex(mutex_);
#else
pthread_mutex_unlock(&mutex_);
#endif
}
// Create new thread.
void SVSync::StartThread(void *(*func)(void*), void* arg) {
#ifdef WIN32
LPTHREAD_START_ROUTINE f = (LPTHREAD_START_ROUTINE) func;
DWORD threadid;
HANDLE newthread = CreateThread(
NULL, // default security attributes
0, // use default stack size
f, // thread function
arg, // argument to thread function
0, // use default creation flags
&threadid); // returns the thread identifier
#else
pthread_t helper;
pthread_create(&helper, NULL, func, arg);
#endif
}
// Place a message in the message buffer (and flush it).
void SVNetwork::Send(const char* msg) {
mutex_send_->Lock();
msg_buffer_out_.append(msg);
mutex_send_->Unlock();
}
// Send the whole buffer.
void SVNetwork::Flush() {
mutex_send_->Lock();
while (msg_buffer_out_.size() > 0) {
int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0);
msg_buffer_out_.erase(0, i);
}
mutex_send_->Unlock();
}
// Receive a message from the server.
// This will always return one line of char* (denoted by \n).
char* SVNetwork::Receive() {
char* result = NULL;
#ifdef WIN32
if (has_content) { result = strtok (NULL, "\n"); }
#else
if (buffer_ptr_ != NULL) { result = strtok_r(NULL, "\n", &buffer_ptr_); }
#endif
// This means there is something left in the buffer and we return it.
if (result != NULL) { return result;
// Otherwise, we read from the stream_.
} else {
buffer_ptr_ = NULL;
has_content = false;
// 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, NULL, NULL, &tv);
// The stream_ died.
if (i == 0) { return NULL; }
// Read the message buffer.
i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0);
// Server quit (0) or error (-1).
if (i <= 0) { return NULL; }
msg_buffer_in_[i] = '\0';
has_content = true;
#ifdef WIN32
return strtok(msg_buffer_in_,"\n");
#else
// Setup a new string tokenizer.
return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_);
#endif
}
}
// Close the connection to the server.
void SVNetwork::Close() {
#ifdef WIN32
closesocket(stream_);
#else
close(stream_);
#endif
}
// Set up a connection to hostname on port.
SVNetwork::SVNetwork(const char* hostname, int port) {
mutex_send_ = new SVMutex();
struct sockaddr_in address;
struct hostent *name;
msg_buffer_in_ = new char[kMaxMsgSize + 1];
msg_buffer_in_[0] = '\0';
has_content = false;
buffer_ptr_ = NULL;
// Get the host data depending on the OS.
#ifdef WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(1, 1), &wsaData);
name = gethostbyname(hostname);
#elif defined(__linux__)
struct hostent hp;
int herr;
char *buffer = new char[kBufferSize];
gethostbyname_r(hostname, &hp, buffer, kBufferSize, &name, &herr);
delete[] buffer;
#else
name = gethostbyname(hostname);
#endif
// Fill in the appropriate variables to be able to connect to the server.
address.sin_family = name->h_addrtype;
memcpy((char *) &address.sin_addr.s_addr,
name->h_addr_list[0], name->h_length);
address.sin_port = htons(port);
stream_ = socket(AF_INET, SOCK_STREAM, 0);
// If server is not there, we will start a new server as local child process.
if (connect(stream_, (struct sockaddr *) &address, sizeof(address)) < 0) {
const char* scrollview_path = getenv("SCROLLVIEW_PATH");
if (scrollview_path == NULL) {
#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
}
// 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* prog = "java -Xms512m -Xmx1024m";
const char* cmd_template = "-Djava.library.path=%s -cp %s/ScrollView.jar;"
"%s/piccolo-1.2.jar;%s/piccolox-1.2.jar"
" com.google.scrollview.ScrollView";
#else
const char* prog = "sh";
const char* cmd_template = "-c \"trap 'kill %1' 0 1 2 ; java "
"-Xms1024m -Xmx2048m -Djava.library.path=%s -cp %s/ScrollView.jar:"
"%s/piccolo-1.2.jar:%s/piccolox-1.2.jar"
" com.google.scrollview.ScrollView"
" >/dev/null 2>&1 & wait\"";
#endif
int cmdlen = strlen(cmd_template) + 4*strlen(scrollview_path) + 1;
char* cmd = new char[cmdlen];
snprintf(cmd, cmdlen, cmd_template, scrollview_path, scrollview_path,
scrollview_path, scrollview_path);
SVSync::StartProcess(prog, cmd);
delete [] cmd;
// Wait for server to show up.
// Note: There is no exception handling in case the server never turns up.
while (connect(stream_, (struct sockaddr *) &address,
sizeof(address)) < 0) {
std::cout << "ScrollView: Waiting for server...\n";
#ifdef WIN32
Sleep(1000);
#else
sleep(1);
#endif
}
}
}
SVNetwork::~SVNetwork() {
delete[] msg_buffer_in_;
delete mutex_send_;
}
#endif // GRAPHICS_DISABLED