#include "hg_ipc.h" #include "../wrapper/hg_log.h" #include "huagao/hgscanner_error.h" #ifdef WIN32 #include "scanner_manager.h" #else #include #include #include #include #include #endif ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // windows event ... #ifdef WIN32 int __stdcall sem_init(sem_t* handle, int, int) { if (!handle) { errno = EINVAL; return -1; } *handle = CreateEvent(NULL, TRUE, FALSE, NULL); if (*handle) return 0; else { errno = GetLastError(); return -1; } } void __stdcall sem_destroy(sem_t* handle) { if (*handle) { CloseHandle(*handle); *handle = NULL; } } int __stdcall sem_trywait(sem_t* handle) { return WaitForSingleObject(*handle, 1) == WAIT_TIMEOUT ? -1 : 0; } void __stdcall sem_wait(sem_t* handle) { if(WaitForSingleObject(*handle, INFINITE) == WAIT_OBJECT_0) ResetEvent(*handle); } int __stdcall sem_timedwait(sem_t* handle, struct timespec* to) { DWORD elapse = to->tv_sec * 1000; elapse += to->tv_nsec / (1000 * 1000); int ret = WaitForSingleObject(*handle, elapse) == WAIT_TIMEOUT ? -1 : 0; if(ret == 0) ResetEvent(*handle); return ret; } void __stdcall sem_post(sem_t* handle) { SetEvent(*handle); } #define pid_t int #endif ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // platform_event (base on semaphore) platform_event::platform_event() : waiting_(false), dbg_info_("") { int err = sem_init(&sem_, 0, 0); if (err == -1) { err = errno; VLOG_MINI_2(LOG_LEVEL_FATAL, "(%s)sem_init failed: %d\n", hg_log::format_ptr(this).c_str(), err); } } platform_event::~platform_event() { sem_destroy(&sem_); } bool platform_event::try_wait(void) { return sem_trywait(&sem_) == 0; } bool platform_event::wait(unsigned timeout) { bool waited = true; VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "platform_event(%s - %s) --> waiting...\n", hg_log::format_ptr(this).c_str(), dbg_info_.c_str()); waiting_ = true; if (timeout == USB_TIMEOUT_INFINITE) sem_wait(&sem_); else { struct timespec to; to.tv_sec = timeout / 1000; to.tv_nsec = (long)((timeout % 1000) * 1000 * 1000); waited = sem_timedwait(&sem_, &to) == 0; } VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "platform_event(%s - %s) --> %s.\n", hg_log::format_ptr(this).c_str(), dbg_info_.c_str(), waited ? "waited" : "wait timeout"); waiting_ = false; return waited; } void platform_event::notify(void) { sem_post(&sem_); } bool platform_event::is_waiting(void) { return waiting_; } void platform_event::set_debug_info(const char* info) { dbg_info_ = info ? info : ""; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // shared_memory shared_memory::shared_memory(unsigned long long key, size_t size) : key_(key), obj_((void*)-1), first_(true), bytes_(size), len_(0) { unsigned int* ptr = (unsigned int*)&key_; VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "shared memory key = 0x%x%08x\n", ptr[1], ptr[0]); init(); } shared_memory::~shared_memory() { clear(); } void shared_memory::init(void) { #ifdef WIN32 char name[40] = { 0 }; DWORD* key = (DWORD*)&key_; HANDLE h = NULL; sprintf(name, "scanner_0x%08x-%08x", key[1], key[0]); h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bytes_, name); if (h == NULL) return; first_ = !(GetLastError() == ERROR_ALREADY_EXISTS); obj_ = (void*)h; #else int obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600); if (obj < 0) { unsigned int* v = (unsigned int*)&key_; if (errno == EEXIST) { first_ = false; obj = shmget(key_, bytes_, 0600); if(obj == -1) obj = shmget(key_, bytes_, 0); VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "open existing: shmget(0x%x%08x) = %d\n", v[1], v[0], obj); obj_ = (void*)obj; std::string prev(read()), proc(""); VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "shared memory content: %s\n", prev.c_str()); if(prev.length()) { proc = prev; size_t pos = proc.find("pid: "); if (pos != std::string::npos) proc.erase(0, pos + 5); pos = proc.find(")"); if (pos != std::string::npos) proc.erase(pos); proc = shared_memory::get_proc_name_by_pid(atoi(proc.c_str())); if (proc.length()) { pos = prev.find("("); if (pos == std::string::npos) pos = prev.length(); if (strcasecmp(proc.c_str(), prev.substr(0, pos).c_str())) proc = ""; } } if (proc.empty()) { first_ = true; clear(); obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600); VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "%s is not existing and reopen it\n", prev.c_str()); } } else { VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "shmget(0x%x%08x) = %d\n", v[1], v[0], errno); return; } } obj_ = (void*)obj; VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "shared memory id = %d[%s], \n", obj, first_ ? "created" : "opened"); #endif if(first_) { pid_t pid = getpid(); std::string me(""); char buf[40] = { 0 }; unsigned int* pn = (unsigned int*)&pid; if (sizeof(pid) > 4 && pn[1]) sprintf(buf, "(pid: 0x%x%08x)", pn[1], pn[0]); else sprintf(buf, "(pid: %u)", pn[0]); hg_log::pe_path(&me); me += buf; write(me.c_str(), me.length()); } } void shared_memory::clear(void) { if (obj_ != (void*)-1) #ifdef WIN32 CloseHandle((HANDLE)obj_); #else { if (first_) { struct shmid_ds ds = { 0 }; int* h = (int*)&obj_; shmctl(*h, IPC_RMID, &ds); } } #endif obj_ = (void*)-1; } char* shared_memory::get_buf(void) { #ifdef WIN32 char* buf = (char*)MapViewOfFile((HANDLE)obj_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!buf) buf = (char*)-1; #else int* h = (int*)&obj_; char* buf = (char*)shmat(*h, 0, 0); VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "shared memory %d tiny_buffer = %s, error = %d\n", *h, hg_log::format_ptr(buf).c_str(), errno); #endif return buf; } void shared_memory::release_buf(void* buf) { #ifdef WIN32 UnmapViewOfFile(buf); #else shmdt(buf); #endif } #ifndef WIN32 std::string shared_memory::get_proc_name_by_pid(pid_t pid) { char path[512] = { 0 }; unsigned int* v = (unsigned int*)&pid; std::string ret(""); if (sizeof(pid) > 4 && v[1]) sprintf(path, "/proc/%lld/status", pid); else sprintf(path, "/proc/%u/status", pid); FILE* src = fopen(path, "rb"); if (src) { char val[512] = { 0 }; memset(path, 0, sizeof(path)); fgets(path, sizeof(path) - 1, src); fclose(src); sscanf(path, "%*s %s", val); ret = val; } if (sizeof(pid) > 4 && v[1]) { VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "PID(%lld) name is: %s\n", pid, ret.c_str()); } else { VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "PID(%u) name is: %s\n", pid, ret.c_str()); } return ret; } #endif bool shared_memory::is_ok(void) { return obj_ != nullptr; } bool shared_memory::is_first(void) { return is_ok() && first_; } std::string shared_memory::read(void) { if (obj_ == (void*)-1) return ""; char* buf = get_buf(); if (buf == (char*)-1) return ""; std::string ret(""); size_t len = 0; int off = sizeof(size_t); memcpy(&len, buf, off); ret = std::string(buf + off, len); release_buf(buf); return ret; } int shared_memory::write(const char* data, size_t len) { if (len > bytes_) return SCANNER_ERR_INSUFFICIENT_MEMORY; char* buf = get_buf(); int off = sizeof(len); if (buf == (char*)-1) return errno; memcpy(buf, &len, off); memcpy(buf + off, data, len); len_ = len; release_buf(buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // tiny_file_map ... #ifdef WIN32 #define IS_VALID_MAP(map) map #else #define IS_VALID_MAP(map) map != -1 #endif tiny_file_map::tiny_file_map() : size_(0), map_(0), buf_(nullptr), file_(""), keep_f_(false) {} tiny_file_map::~tiny_file_map() { close(); } int tiny_file_map::open(const char* file, unsigned int size, bool readonly) { close(); int ret = 0; #ifdef WIN32 if (readonly) { HANDLE f = CreateFileA(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (f == INVALID_HANDLE_VALUE) ret = GetLastError(); else { size_ = GetFileSize(f, NULL); map_ = CreateFileMappingA(f, NULL, PAGE_READONLY, 0, 0, NULL); if (map_) { buf_ = (unsigned char*)MapViewOfFile(map_, FILE_MAP_READ, 0, 0, 0); if (!buf_) { ret = GetLastError(); CloseHandle(map_); map_ = nullptr; size_ = 0; } } else ret = GetLastError(); CloseHandle(f); } } else { size_ = size; HANDLE f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (f == INVALID_HANDLE_VALUE) ret = GetLastError(); else { //SYSTEM_INFO si = { 0 }; //GetSystemInfo(&si); //size += si.dwPageSize - 1; //size /= si.dwPageSize; //size *= si.dwPageSize; DWORD wrote = SetFilePointer(f, size - 1, NULL, FILE_BEGIN); if (wrote == size - 1) WriteFile(f, "\0", 1, &wrote, NULL); map_ = CreateFileMappingA(f, NULL, PAGE_READWRITE, 0, size, NULL); if (map_) { buf_ = (unsigned char*)MapViewOfFile(map_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size); if (!buf_) ret = GetLastError(); } else ret = GetLastError(); CloseHandle(f); if (ret) { keep_f_ = false; close(); } } } #else if (readonly) { map_ = ::open(file, O_RDONLY); if (IS_VALID_MAP(map_)) { struct stat fsize; if (fstat(map_, &fsize) >= 0) size_ = size = fsize.st_size; } } else { map_ = ::open(file, O_CREAT | O_RDWR); if (IS_VALID_MAP(map_)) { if (lseek(map_, size - 1, SEEK_SET) < 0) { VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "set file size to %u - 1 bytes failed: %d\n", size, errno); ret = errno; ::close(map_); map_ = 0; remove(file); return ret; } if (write(map_, "0", 1) < 0) { VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "set file size to %u bytes failed: %d\n", size, errno); ret = errno; ::close(map_); map_ = 0; remove(file); return ret; } } } if (IS_VALID_MAP(map_)) { buf_ = (unsigned char*)mmap(nullptr, size, readonly ? PROT_READ : PROT_WRITE, MAP_SHARED, map_, 0); if (buf_ == (unsigned char*)MAP_FAILED) { buf_ = nullptr; ret = errno; VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "mmap(%u) = %d\n", size, ret); size_ = 0; if (!readonly) remove(file); } ::close(map_); map_ = 0; } else ret = errno; #endif VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "map([%s]%s) = %d\n", readonly ? "R" : "RW", file, ret); if (ret == 0) file_ = file; return ret; } void tiny_file_map::close(void) { if (buf_) { #ifdef WIN32 UnmapViewOfFile(buf_); #else munmap(buf_, size_); #endif buf_ = nullptr; } if (map_) { #ifdef WIN32 CloseHandle(map_); #else ::close(map_); #endif map_ = 0; } if (!keep_f_ && !file_.empty()) remove(file_.c_str()); size_ = 0; file_ = ""; keep_f_ = false; } void tiny_file_map::keep_file(bool keep) { keep_f_ = keep; } unsigned char* tiny_file_map::mapping_buffer(void) { return buf_; } std::string tiny_file_map::file(void) { return file_; } unsigned int tiny_file_map::size(void) { return size_; } bool tiny_file_map::swap(void) { bool ret = true; if (buf_) { #ifdef WIN32 UnmapViewOfFile(buf_); #else munmap(buf_, size_); #endif buf_ = nullptr; } else if (IS_VALID_MAP(map_)) { #ifdef WIN32 buf_ = (unsigned char*)MapViewOfFile(map_, FILE_MAP_READ, 0, 0, size_); #else buf_ = (unsigned char*)mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, map_, 0); if (buf_ == (unsigned char*)MAP_FAILED) { buf_ == nullptr; VLOG_MINI_2(LOG_LEVEL_FATAL, "Remap '%s' failed: %d\n", file_.c_str(), errno); } #endif ret = buf_ != nullptr; } else { VLOG_MINI_2(LOG_LEVEL_FATAL, "Remap '%s' bug the handle is %d\n", file_.c_str(), map_); ret = false; } return ret; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // memory or file mapping ... tiny_buffer::tiny_buffer(unsigned int size , const char* tmp_path , const char* name_leading , const char* ext , unsigned int uniq_id) : size_(size), buf_(nullptr) { init(tmp_path, name_leading, ext, uniq_id); } tiny_buffer::tiny_buffer(const char* src_file) { fmap_.open(src_file, 0, true); buf_ = fmap_.mapping_buffer(); size_ = fmap_.size(); } tiny_buffer::~tiny_buffer() { if (buf_) { if (fmap_.file().empty()) delete[] buf_; else fmap_.close(); } } void tiny_buffer::init(const char* tmp_path, const char* name_leading, const char* ext, unsigned int uniq_id) { try { buf_ = new unsigned char[size_]; memset(buf_, 0, size_); } catch (...) { std::string f(tmp_path); char buf[128] = { 0 }; f += PATH_SEPARATOR; f += name_leading ? name_leading : "mapf"; sprintf(buf, "_%05u.%s", uniq_id, ext ? ext : "tmp"); f += buf; fmap_.open(f.c_str(), size_, false); buf_ = fmap_.mapping_buffer(); } // VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "Acquire memory with bytes %u = %s\n", size_, hg_log::format_ptr(buf_).c_str()); } unsigned int tiny_buffer::size(void) { return size_; } unsigned char* tiny_buffer::data(void) { return buf_; } void tiny_buffer::keep_file(bool keep) { fmap_.keep_file(keep); } std::string tiny_buffer::file(void) { return fmap_.file(); } bool tiny_buffer::swap(void) { if (fmap_.file().empty()) return true; bool ret = fmap_.swap(); buf_ = fmap_.mapping_buffer(); return ret; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // final_img_queue final_img_queue::final_img_queue() {} final_img_queue::~final_img_queue() {} size_t final_img_queue::size(void) { std::lock_guard lck(lock_); return queue_.size(); } void final_img_queue::clear(void) { std::lock_guard lck(lock_); queue_.clear(); } bool final_img_queue::put(int w, int h, int bpp, int channels, int line_bytes, void* data, unsigned bytes , const char* tmp_path, const char* name_leading, const char* ext, int ind) { IMGDT imgd; bool ret = false; imgd.header.bits = bpp; imgd.header.bytes = bytes; imgd.header.channels = channels; imgd.header.height = h; imgd.header.line_bytes = line_bytes; imgd.header.width = w; imgd.offset = 0; imgd.data.reset(new tiny_buffer(bytes, tmp_path, name_leading, ext, ind)); if(imgd.data->data()) { memcpy(imgd.data->data(), data, bytes); if (imgd.data->swap()) { std::lock_guard lck(lock_); queue_.push_back(imgd); ret = true; } else imgd.data.reset(); } return ret; } bool final_img_queue::front(IMH* header) { std::lock_guard lck(lock_); if (queue_.size() == 0) return false; memcpy(header, &queue_[0].header, sizeof(*header)); return true; } void final_img_queue::fetch_front(void* buf, int* len, bool* over) { std::lock_guard lck(lock_); if (queue_.size() == 0) { if(len) *len = 0; if(over) *over = true; } else { IMGDT& imgd = queue_[0]; if (imgd.offset == 0) { if (!imgd.data->swap()) { *len = 0; if (over) *over = true; VLOG_MINI_1(LOG_LEVEL_FATAL, "Reload final image '%s' failed!\n", imgd.data->file().c_str()); return; } } if (imgd.offset + *len >= imgd.header.bytes) *len = imgd.header.bytes - imgd.offset; memcpy(buf, imgd.data->data() + imgd.offset, *len); imgd.offset += *len; if (imgd.offset >= imgd.header.bytes) { if (over) *over = true; queue_.erase(queue_.begin()); } } }