#include "hg_ipc.h" #include "../wrapper/hg_log.h" #include "huagao/hgscanner_error.h" #if defined(WIN32) || defined(_WIN64) //#include "scanner_manager.h" #else #include #include #include #include #include #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // memory management ... void* allocate_memory(size_t bytes, const char* log_msg) { bytes += 7; bytes /= 8; bytes *= 8; return new char[bytes]; } void free_memory(void* ptr) { if (ptr) delete[] ptr; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // windows event ... #if defined(WIN32) || defined(_WIN64) 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) { #if defined(WIN32) || defined(_WIN64) 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 | 0666); 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) #if defined(WIN32) || defined(_WIN64) 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) { #if defined(WIN32) || defined(_WIN64) 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 buffer = %s, error = %d\n", *h, hg_log::format_ptr(buf).c_str(), errno); #endif return buf; } void shared_memory::release_buf(void* buf) { #if defined(WIN32) || defined(_WIN64) UnmapViewOfFile(buf); #else shmdt(buf); #endif } #if !defined(WIN32) && !defined(_WIN64) 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_ != (void*)-1; } 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 = 4; // 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 = 4; // sizeof(len); if (buf == (char*)-1) return errno; memcpy(buf, &len, off); memcpy(buf + off, data, len); len_ = len; release_buf(buf); return 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // tiny_file_map ... tiny_file_map::tiny_file_map() : size_(0), map_(INVALID_HANDLE_NAME), buf_(nullptr), file_(""), keep_f_(false) , map_off_(0), map_bytes_(0), page_size_(hg_log::get_page_size()) { hg_log::get_page_size(&page_size_); } tiny_file_map::~tiny_file_map() { close(); } HANDLE_NAME tiny_file_map::open_file_for_mapping(const char* file, unsigned* bytes, bool create) { HANDLE_NAME ret = INVALID_HANDLE_NAME; #if defined(WIN32) || defined(_WIN64) HANDLE f = INVALID_HANDLE_VALUE; if (create) { f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (f != INVALID_HANDLE_VALUE) { DWORD wrote = SetFilePointer(f, *bytes - 1, NULL, FILE_BEGIN); if (wrote != *bytes - 1 || !WriteFile(f, "\0", 1, &wrote, NULL)) { CloseHandle(f); f = INVALID_HANDLE_VALUE; } } } else { f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (f != INVALID_HANDLE_VALUE) *bytes = GetFileSize(f, NULL); } if (f != INVALID_HANDLE_VALUE) { ret = CreateFileMapping(f, NULL, PAGE_READWRITE, 0, *bytes, NULL); CloseHandle(f); } #else if (create) { ret = ::open(file, O_CREAT | O_RDWR,0777); if (ret != INVALID_HANDLE_NAME) { if (lseek(ret, *bytes - 1, SEEK_SET) < 0) { VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "set file size to %u - 1 bytes failed: %d\n", *bytes, errno); ::close(ret); remove(file); ret = INVALID_HANDLE_NAME; } if (write(ret, "0", 1) < 0) { VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "set file size to %u bytes failed: %d\n", *bytes, errno); ::close(ret); remove(file); ret = INVALID_HANDLE_NAME; } } } else { ret = ::open(file, O_RDWR); if (ret != INVALID_HANDLE_NAME) { struct stat fsize; if (fstat(ret, &fsize) >= 0) *bytes = fsize.st_size; } } #endif return ret; } void tiny_file_map::close_handle_name(HANDLE_NAME h) { #if defined(WIN32) || defined(_WIN64) CloseHandle(h); #else ::close(h); #endif } void* tiny_file_map::sys_map_api(HANDLE_NAME h, int access, unsigned int off, unsigned size, int* err) { void* mem = nullptr; #if defined(WIN32) || defined(_WIN64) mem = MapViewOfFile(h, access, 0, off, size); if (err) { if (mem) *err = SCANNER_ERR_OK; else { if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) *err = SCANNER_ERR_INSUFFICIENT_MEMORY; else *err = SCANNER_ERR_OUT_OF_RANGE; } } #else mem = mmap(nullptr, size, access, MAP_SHARED, h, off); if (mem == MAP_FAILED) { mem = nullptr; if (errno == ENOMEM) *err = SCANNER_ERR_INSUFFICIENT_MEMORY; else *err = SCANNER_ERR_OUT_OF_RANGE; } else if(err) *err = SCANNER_ERR_OK; #endif return mem; } void tiny_file_map::sys_unmap_api(void* buf, size_t size) { #if defined(WIN32) || defined(_WIN64) UnmapViewOfFile(buf); #else munmap(buf, size); #endif } int tiny_file_map::map_to_mem(unsigned int off) { int err = SCANNER_ERR_OUT_OF_RANGE; #if defined(WIN32) || defined(_WIN64) int acc = FILE_MAP_READ | FILE_MAP_WRITE; #else int acc = PROT_READ | PROT_WRITE; #endif if (off < size_) { unsigned int bytes = size_ - off; if (off >= map_off_ && off + bytes <= map_off_ + map_bytes_) err = SCANNER_ERR_OK; else { if (buf_) tiny_file_map::sys_unmap_api(buf_, map_bytes_); off /= page_size_; off *= page_size_; map_bytes_ = bytes; map_off_ = off; buf_ = (unsigned char*)tiny_file_map::sys_map_api(map_, acc, map_off_, map_bytes_, &err); // MapViewOfFile(map_, FILE_MAP_READ | FILE_MAP_WRITE, 0, off, map_bytes_); if (err != SCANNER_ERR_OK) { map_bytes_ /= page_size_; map_bytes_ *= page_size_; while (map_bytes_ >= page_size_ && !(buf_ = (unsigned char*)tiny_file_map::sys_map_api(map_, acc, map_off_, map_bytes_, &err)) && err == SCANNER_ERR_INSUFFICIENT_MEMORY) map_bytes_ -= page_size_; } } } return err; } int tiny_file_map::open(const char* file, bool existing, unsigned int size) { int ret = SCANNER_ERR_INSUFFICIENT_MEMORY; close(); map_ = tiny_file_map::open_file_for_mapping(file, &size, !existing); if (map_ != INVALID_HANDLE_NAME) { ret = SCANNER_ERR_OK; size_ = size; } VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "map([%s]%s) = %d\n", existing ? "existing" : "new", file, ret); if (ret == SCANNER_ERR_OK) file_ = file; return ret; } void tiny_file_map::close(void) { if (buf_) { tiny_file_map::sys_unmap_api(buf_, size_); buf_ = nullptr; } if (map_ != INVALID_HANDLE_NAME) { close_handle_name(map_); map_ = INVALID_HANDLE_NAME; } if (!keep_f_ && !file_.empty()) remove(file_.c_str()); size_ = 0; file_ = ""; keep_f_ = false; map_off_ = map_bytes_ = 0; } void tiny_file_map::keep_file(bool keep) { keep_f_ = keep; } unsigned char* tiny_file_map::mapping_buffer(unsigned int off, unsigned int* bytes) { unsigned int len = bytes ? *bytes : size_; unsigned char* buf = nullptr; if (off >= size_) { return buf; } if (!buf_ && map_to_mem(off) != SCANNER_ERR_OK) { return buf; } if (off >= map_off_ && off + len <= map_off_ + map_bytes_) { buf = buf_ + off - map_off_; return buf; } if (off < map_off_ || off >= map_off_ + map_bytes_) { if (map_to_mem(off) == SCANNER_ERR_OK) { buf = buf_ + off - map_off_; if (bytes) *bytes = map_bytes_ - (off - map_off_); } return buf; } // buf = buf_ + off - map_off_; if (bytes) *bytes = map_bytes_ - (off - map_off_); 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; tiny_file_map::sys_unmap_api(buf_, size_); buf_ = nullptr; 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), img_statu_(IMG_STATUS_OK) { init(tmp_path, name_leading, ext, uniq_id); } tiny_buffer::tiny_buffer(const char* src_file) : size_(0), buf_(nullptr) { fmap_.open(src_file); size_ = fmap_.size(); unsigned int len = size_; buf_ = fmap_.mapping_buffer(0, &len); } 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 (...) { if (tmp_path && *tmp_path) { 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; unsigned int bytes = size_; fmap_.open(f.c_str(), false, size_); buf_ = fmap_.mapping_buffer(0, &bytes); } } } unsigned int tiny_buffer::size(void) { return size_; } unsigned char* tiny_buffer::data(unsigned int off, unsigned int* bytes) { if (off >= size_) return nullptr; if (fmap_.file().empty()) { if (size_ - off < *bytes) *bytes = size_ - off; return buf_ + off; } return fmap_.mapping_buffer(off, bytes); } 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(); unsigned int bytes = size_; buf_ = fmap_.mapping_buffer(0, &bytes); return ret; } int tiny_buffer::to_file(const char* file) { FILE* dst = fopen(file, "wb"); if (!dst) return errno; unsigned int off = 0, len = size_; unsigned char* buf = data(off, &len); while (buf) { fwrite(buf, 1, len, dst); off += len; if (off >= size_) break; len = size_ - off; buf = data(off, &len); } fclose(dst); return 0; } void tiny_buffer::set_image_statu(int statu) { img_statu_ = statu; } int tiny_buffer::get_image_statu(void) { return img_statu_; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // final_img_queue final_img_queue::final_img_queue() : mem_usage_(0) {} final_img_queue::~final_img_queue() {} unsigned long long final_img_queue::mem_usage(void) { return mem_usage_; } 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_); mem_usage_ = 0; queue_.clear(); } bool final_img_queue::put(int w, int h, int bpp, int channels, int line_bytes, void* data, unsigned bytes, int statu , const char* tmp_path, const char* name_leading, const char* ext, int ind, uint32_t id) { IMGDT imgd; bool ret = false; unsigned int l = bytes, off = 0; 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.header.src_id = id; imgd.header.statu = statu; imgd.offset = 0; imgd.data.reset(new tiny_buffer(bytes, tmp_path, name_leading, ext, ind)); unsigned char* buf = imgd.data->data(off, &l), * src = (unsigned char*)data; while(buf) { memcpy(buf, src, l); off += l; if (off >= bytes) break; src += l; l = bytes - off; buf = imgd.data->data(off, &l); } if (off >= bytes && imgd.data->swap()) { std::lock_guard lck(lock_); queue_.push_back(imgd); mem_usage_ += bytes; 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 { // for third-apps, we make fake data upto len when re-map file failed here IMGDT& imgd = queue_[0]; if (imgd.offset == 0) { if (!imgd.data->swap()) { VLOG_MINI_1(LOG_LEVEL_FATAL, "Reload final image '%s' failed!\n", imgd.data->file().c_str()); } } if (imgd.offset + *len >= imgd.header.bytes) *len = imgd.header.bytes - imgd.offset; unsigned char* src = imgd.data->data(imgd.offset, (unsigned int*)len); if (src) { memcpy(buf, src, *len); } else { VLOG_MINI_2(LOG_LEVEL_FATAL, "Remap final image '%s + 0x%08x' failed!\n", imgd.data->file().c_str(), imgd.offset); } imgd.offset += *len; if (imgd.offset >= imgd.header.bytes) { mem_usage_ -= imgd.header.bytes; if (mem_usage_ < 0) mem_usage_ = 0; if (over) *over = true; queue_.erase(queue_.begin()); } } }