使用线程池优化扫描仪发现接口

Signed-off-by: 刘昊苏 <liuhaosu@huawei.com>
This commit is contained in:
刘昊苏
2025-11-13 14:41:32 +08:00
parent 007bc5c729
commit 34e180823c
11 changed files with 574 additions and 32 deletions
+46 -1
View File
@@ -21,6 +21,8 @@ SANE_LIB_DIR = "/data/service/el1/public/print_service/sane/backend"
SANE_V_MAJOR = 1
SANE_V_MINOR = 2
enable_hilog = true
enable_thread_pool = true
enable_scan_service = true
source_dir = "//third_party/backends"
target_dir = "${target_gen_dir}/sane"
@@ -29,7 +31,8 @@ action("backends_action") {
script = "//third_party/backends/install.py"
inputs = [
"${source_dir}/patches/modifying_driver_search_path.patch",
"${source_dir}/patches/dll.c.patch",
"${source_dir}/patches/add_thread_poll.patch",
"${source_dir}/patches/hilog_debug.patch",
]
outputs = [
"${target_dir}/include/config.h",
@@ -69,6 +72,14 @@ config("backends_private_config") {
cflags += [ "-DENABLE_HILOG" ]
}
if (enable_thread_pool) {
cflags += [ "-DHAVE_THREAD_POLL"]
}
if (enable_scan_service) {
cflags += [ "-DHAVE_SCAN_SERVICE"]
}
defines = [
"PATH_SANE_CONFIG_DIR=$SANE_CONFIG_DIR",
"PATH_SANE_DATA_DIR=$SANE_DATA_DIR",
@@ -185,6 +196,33 @@ ohos_source_set("sane_strstatus") {
part_name = "backends"
}
ohos_source_set("threadpool_c") {
sources = [
"threadpool/src/threadpool_c.cpp",
"threadpool/src/threadpool_wrapper.cpp"
]
include_dirs = [
"threadpool/include",
]
external_deps = [
"c_utils:utils",
]
if (enable_hilog) {
external_deps += [ "hilog:libhilog" ]
}
public_configs = [ ":backends_public_config" ]
configs = [ ":backends_private_config" ]
subsystem_name = "thirdparty"
part_name = "backends"
}
ohos_shared_library("sane") {
sources = [
"./backend/dll.c",
@@ -207,6 +245,13 @@ ohos_shared_library("sane") {
":sanei_usb",
]
include_dirs = []
if (enable_thread_pool) {
deps += [ ":threadpool_c" ]
include_dirs += [ "threadpool/include" ]
}
if (enable_hilog) {
external_deps = [ "hilog:libhilog" ]
}
+1
View File
@@ -23,6 +23,7 @@
"deps": {
"components": [
"hilog",
"c_utils",
"libusb"
],
"third_party": [
+2 -1
View File
@@ -24,7 +24,8 @@ import shutil
def apply_patch(source_dir):
patch_list = [
'modifying_driver_search_path.patch',
'dll.c.patch',
'add_thread_poll.patch',
'hilog_debug.patch',
]
for patch in patch_list:
+269
View File
@@ -0,0 +1,269 @@
diff --git a/backend/dll.c b/backend/dll.c
index 7a505d13f..120dcf7e5 100644
--- a/backend/dll.c
+++ b/backend/dll.c
@@ -292,6 +292,26 @@ static const char *op_name[] = {
};
#endif /* __BEOS__ */
+#ifdef HAVE_THREAD_POLL
+
+#include "threadpool_c.h"
+
+typedef struct backend_task {
+ struct backend *be;
+ SANE_Bool local_only;
+ const SANE_Device **be_list;
+ SANE_Status status;
+ int num_devs;
+ SANE_Device **devices;
+ int device_count;
+} backend_task_t;
+
+static threadpool_handle_t* pool = NULL;
+
+static const int max_thread_number = 20;
+
+#endif // HAVE_THREAD_POLL
+
#if defined(HAVE_SCAN_SERVICE)
static int has_suffix(const char *filename, const char *suffix)
{
@@ -1052,6 +1072,13 @@ sane_exit (void)
DBG (2, "sane_exit: exiting\n");
+#ifdef HAVE_THREAD_POLL
+if (pool) {
+ threadpool_destroy(pool);
+ pool = NULL;
+}
+#endif // HAVE_THREAD_POLL
+
for (be = first_backend; be; be = next)
{
next = be->next;
@@ -1123,6 +1150,7 @@ sane_exit (void)
DBG (3, "sane_exit: finished\n");
}
+#ifndef HAVE_THREAD_POLL
/* Note that a call to get_devices() implies that we'll have to load
all backends. To avoid this, you can call sane_open() directly
(assuming you know the name of the backend/device). This is
@@ -1238,6 +1266,215 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
return SANE_STATUS_GOOD;
}
+#else
+static void process_backend_task(void *arg) {
+ backend_task_t *task = (backend_task_t *)arg;
+ if (!task || !task->be) {
+ DBG (1, "process_backend_task: parameter is null\n");
+ return;
+ }
+ struct backend *be = task->be;
+ SANE_Status status;
+ char *full_name;
+ int i, num_devs;
+ size_t len;
+
+ if (!be->inited) {
+ if (init(be) != SANE_STATUS_GOOD) {
+ task->status = SANE_STATUS_INVAL;
+ return;
+ }
+ }
+ if (be->name) {
+ DBG (1, "process_backend_task: driver [%s] sane_get_devices begin\n", be->name);
+ }
+ status = (*(op_get_devs_t)be->op[OP_GET_DEVS]) (&task->be_list, task->local_only);
+ if (be->name) {
+ DBG (1, "process_backend_task: driver [%s] sane_get_devices end\n", be->name);
+ }
+ task->status = status;
+
+ if (status != SANE_STATUS_GOOD || !task->be_list) {
+ DBG (1, "process_backend_task: driver [%s] sane_get_devices fail, ret = %d\n", be->name, (int)status);
+ return;
+ }
+
+ for (task->num_devs = 0; task->be_list[task->num_devs]; ++task->num_devs);
+
+ task->devices = malloc(task->num_devs * sizeof(SANE_Device*));
+ if (!task->devices) {
+ task->status = SANE_STATUS_NO_MEM;
+ return;
+ }
+ task->device_count = 0;
+
+ for (i = 0; i < task->num_devs; ++i) {
+ SANE_Device *dev;
+ char *mem;
+ struct alias *alias;
+
+ for (alias = first_alias; alias != NULL; alias = alias->next) {
+ len = strlen(be->name);
+ if (strlen(alias->oldname) <= len)
+ continue;
+ if (strncmp(alias->oldname, be->name, len) == 0
+ && alias->oldname[len] == ':'
+ && strcmp(&alias->oldname[len + 1], task->be_list[i]->name) == 0)
+ break;
+ }
+
+ if (alias) {
+ if (!alias->newname) {
+ continue;
+ }
+
+ len = strlen(alias->newname);
+ mem = malloc(sizeof(*dev) + len + 1);
+ if (!mem) {
+ task->status = SANE_STATUS_NO_MEM;
+ for (int j = 0; j < task->device_count; j++) {
+ free(task->devices[j]);
+ }
+ free(task->devices);
+ task->devices = NULL;
+ return;
+ }
+
+ full_name = mem + sizeof(*dev);
+ strcpy(full_name, alias->newname);
+ } else {
+ len = strlen(be->name) + 1 + strlen(task->be_list[i]->name);
+ mem = malloc(sizeof(*dev) + len + 1);
+ if (!mem) {
+ task->status = SANE_STATUS_NO_MEM;
+ for (int j = 0; j < task->device_count; j++) {
+ free(task->devices[j]);
+ }
+ free(task->devices);
+ task->devices = NULL;
+ return;
+ }
+
+ full_name = mem + sizeof(*dev);
+ strcpy(full_name, be->name);
+ strcat(full_name, ":");
+ strcat(full_name, task->be_list[i]->name);
+ }
+
+ dev = (SANE_Device *) mem;
+ dev->name = full_name;
+ dev->vendor = task->be_list[i]->vendor;
+ dev->model = task->be_list[i]->model;
+ dev->type = task->be_list[i]->type;
+
+ task->devices[task->device_count++] = dev;
+ }
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ const SANE_Device **be_list;
+ struct backend *be;
+ SANE_Status status;
+ char *full_name;
+ int i, num_devs;
+ size_t len;
+ #define ASSERT_SPACE(n) do \
+ { \
+ if (devlist_len + (n) > devlist_size) \
+ { \
+ devlist_size += (n) + 15; \
+ if (devlist) \
+ devlist = realloc (devlist, devlist_size * sizeof (devlist[0])); \
+ else \
+ devlist = malloc (devlist_size * sizeof (devlist[0])); \
+ if (!devlist) \
+ return SANE_STATUS_NO_MEM; \
+ } \
+ } while (0)
+
+ DBG (3, "sane_get_devices\n");
+
+ if (devlist)
+ for (i = 0; i < devlist_len; ++i)
+ free ((void *) devlist[i]);
+ devlist_len = 0;
+
+ int backend_count = 0;
+ for (be = first_backend; be; be = be->next) {
+ backend_count++;
+ }
+
+ if (backend_count == 0) {
+ ASSERT_SPACE (1);
+ devlist[devlist_len++] = 0;
+ *device_list = (const SANE_Device **) devlist;
+ return SANE_STATUS_GOOD;
+ }
+
+ backend_task_t *tasks = malloc(backend_count * sizeof(backend_task_t));
+ if (!tasks) {
+ return SANE_STATUS_NO_MEM;
+ }
+ if (pool == NULL) {
+ int discoverThreadNum = backend_count < max_thread_number ? backend_count : max_thread_number;
+ pool = threadpool_create(discoverThreadNum);
+ }
+ int task_index = 0;
+ for (be = first_backend; be; be = be->next, task_index++) {
+ tasks[task_index].be = be;
+ tasks[task_index].local_only = local_only;
+ tasks[task_index].be_list = NULL;
+ tasks[task_index].status = SANE_STATUS_GOOD;
+ tasks[task_index].num_devs = 0;
+ tasks[task_index].devices = NULL;
+ tasks[task_index].device_count = 0;
+
+ if (!threadpool_add_task(pool, process_backend_task, &tasks[task_index])) {
+ DBG(1, "Failed to add task for backend %s\n", be->name);
+ tasks[task_index].status = SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ threadpool_wait(pool);
+
+ int total_devices = 0;
+ for (task_index = 0; task_index < backend_count; task_index++) {
+ if (tasks[task_index].status == SANE_STATUS_GOOD && tasks[task_index].devices) {
+ total_devices += tasks[task_index].device_count;
+ }
+ }
+
+ ASSERT_SPACE(total_devices);
+
+ for (task_index = 0; task_index < backend_count; task_index++) {
+ backend_task_t *task = &tasks[task_index];
+
+ if (task->status != SANE_STATUS_GOOD || !task->devices) {
+ continue;
+ }
+
+ for (i = 0; i < task->device_count; i++) {
+ devlist[devlist_len++] = task->devices[i];
+ }
+
+ free(task->devices);
+ }
+
+ free(tasks);
+ threadpool_destroy(pool);
+ pool = NULL;
+
+ ASSERT_SPACE (1);
+ devlist[devlist_len++] = 0;
+
+ *device_list = (const SANE_Device **) devlist;
+ DBG (3, "sane_get_devices: found %d devices\n", devlist_len - 1);
+ return SANE_STATUS_GOOD;
+}
+#endif // HAVE_THREAD_POLL
+
SANE_Status
sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
{
-17
View File
@@ -1,17 +0,0 @@
diff --git a/backend/dll.c b/backend/dll.c
index 9b13d3a59..7a505d13f 100644
--- a/backend/dll.c
+++ b/backend/dll.c
@@ -1353,6 +1353,12 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
}
free(be_name);
+ if (!be)
+ {
+ DBG (3, "sane_open: be is nullptr\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
if (!be->inited)
{
status = init (be);
+27
View File
@@ -0,0 +1,27 @@
diff --git a/include/sane/sanei_debug.h b/include/sane/sanei_debug.h
index 465d3e25e..b18da651c 100644
--- a/include/sane/sanei_debug.h
+++ b/include/sane/sanei_debug.h
@@ -8,7 +8,9 @@
#define _SANEI_DEBUG_H
#include <sane/sanei.h>
-
+#ifdef ENABLE_HILOG
+#include "hilog/log.h"
+#endif // ENABLE_HILOG
#ifdef __cplusplus
extern "C" {
#endif
@@ -79,7 +81,11 @@ extern void sanei_debug_ndebug (int level, const char *msg, ...);
# define DBG_LEVEL (0)
# define DBG_INIT()
+#ifndef ENABLE_HILOG
# define DBG sanei_debug_ndebug
+#else
+# define DBG(level, ...) ((void)HiLogPrint(LOG_APP, LOG_INFO, 0, "sanekit", __VA_ARGS__))
+#endif // ENABLE_HILOG
# define IF_DBG(x)
#else /* !NDEBUG */
+12 -13
View File
@@ -1,5 +1,5 @@
diff --git a/backend/dll.c b/backend/dll.c
index bf34c4f6d..9b13d3a59 100644
index bf34c4f6d..7a505d13f 100644
--- a/backend/dll.c
+++ b/backend/dll.c
@@ -292,6 +292,46 @@ static const char *op_name[] = {
@@ -102,17 +102,16 @@ index bf34c4f6d..9b13d3a59 100644
fp = sanei_config_open (DLL_ALIASES_FILE);
if (!fp)
return SANE_STATUS_GOOD; /* don't insist on aliases file */
diff --git a/include/sane/config.h b/include/sane/config.h
index 20dfa104b..206a4147e 100644
--- a/include/sane/config.h
+++ b/include/sane/config.h
@@ -209,6 +209,9 @@
/* Define to 1 if you have libusb-1.0 */
#define HAVE_LIBUSB 1
@@ -1284,6 +1353,12 @@ sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle)
}
free(be_name);
+/* Define to 1 if you have scan_service */
+#define HAVE_SCAN_SERVICE 1
+ if (!be)
+ {
+ DBG (3, "sane_open: be is nullptr\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
/* Define to 1 if you have libusb-0.1 */
/* #undef HAVE_LIBUSB_LEGACY */
if (!be->inited)
{
status = init (be);
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef THREADPOOL_C_H
#define THREADPOOL_C_H
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*threadpool_task_func_t)(void* arg);
typedef struct threadpool_handle_t threadpool_handle_t;
threadpool_handle_t* threadpool_create(int thread_num);
bool threadpool_add_task(threadpool_handle_t* pool, threadpool_task_func_t func, void* arg);
void threadpool_destroy(threadpool_handle_t* pool);
void threadpool_wait(threadpool_handle_t* pool);
#ifdef __cplusplus
}
#endif
#endif // THREADPOOL_C_H
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef THREADPOOL_WRAPPER
#define THREADPOOL_WRAPPER
#include "threadpool_c.h"
#include "thread_pool.h"
#include <string>
class ThreadPoolWrapper {
public:
ThreadPoolWrapper() = default;
~ThreadPoolWrapper() = default;
bool Start(int thread_num);
void Stop();
void AddTask(threadpool_task_func_t func, void* arg);
void Wait();
private:
OHOS::ThreadPool pool_;
};
#endif // THREADPOOL_WRAPPER
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "threadpool_c.h"
#include "threadpool_wrapper.h"
#include "sanei_debug.h"
#define LEVEL 1
struct threadpool_handle_t {
ThreadPoolWrapper* wrapper;
};
threadpool_handle_t* threadpool_create(int32_t thread_num)
{
constexpr int32_t maxThreadNum = 20;
if (thread_num <= 0 || thread_num > maxThreadNum) {
DBG(LEVEL, "threadpool_create: fail\n");
return nullptr;
}
threadpool_handle_t* handle = new(std::nothrow) threadpool_handle_t();
if (!handle) {
DBG(LEVEL, "threadpool_create: handle is nullptr\n");
return nullptr;
}
handle->wrapper = new(std::nothrow) ThreadPoolWrapper();
if (!handle->wrapper) {
delete handle;
DBG(LEVEL, "threadpool_create: wrapper is nullptr\n");
return nullptr;
}
if (!handle->wrapper->Start(thread_num)) {
delete handle->wrapper;
delete handle;
DBG(LEVEL, "threadpool_create: Start fail\n");
return nullptr;
}
return handle;
}
bool threadpool_add_task(threadpool_handle_t* pool, threadpool_task_func_t func, void* arg)
{
if (!pool || !pool->wrapper || !func) {
DBG(LEVEL, "threadpool_add_task: fail\n");
return false;
}
pool->wrapper->AddTask(func, arg);
return true;
}
void threadpool_destroy(threadpool_handle_t* pool)
{
if (pool) {
if (pool->wrapper) {
delete pool->wrapper;
}
delete pool;
}
}
void threadpool_wait(threadpool_handle_t* pool)
{
if (pool && pool->wrapper) {
pool->wrapper->Wait();
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <functional>
#include <string>
#include "errors.h"
#include "threadpool_wrapper.h"
bool ThreadPoolWrapper::Start(int32_t thread_num)
{
return pool_.Start(thread_num) == OHOS::ERR_OK;
}
void ThreadPoolWrapper::Stop()
{
pool_.Stop();
}
void ThreadPoolWrapper::AddTask(threadpool_task_func_t func, void* arg)
{
if (func) {
pool_.AddTask([func, arg]() {
func(arg);
});
}
}
void ThreadPoolWrapper::Wait()
{
constexpr int32_t WAIT_TIME = 1;
int32_t curTaskNum = 0;
do {
curTaskNum = static_cast<int32_t>(pool_.GetCurTaskNum());
std::this_thread::sleep_for(std::chrono::seconds(WAIT_TIME));
} while (curTaskNum != 0);
pool_.Stop();
}