/* * soft_worker.cpp - soft worker implementation * * Copyright (c) 2017 Intel Corporation * * 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. * * Author: Wind Yuan */ #include "soft_worker.h" #include "thread_pool.h" #include "xcam_mutex.h" namespace XCam { class ItemSynch { private: mutable std::atomic _remain_items; Mutex _mutex; XCamReturn _error; public: ItemSynch (uint32_t items) : _remain_items(items), _error (XCAM_RETURN_NO_ERROR) {} void update_error (XCamReturn err) { SmartLock locker(_mutex); _error = err; } XCamReturn get_error () { SmartLock locker(_mutex); return _error; } uint32_t dec() { return --_remain_items; } private: XCAM_DEAD_COPY (ItemSynch); }; class WorkItem : public ThreadPool::UserData { public: WorkItem ( const SmartPtr &worker, const SmartPtr &args, const WorkSize &item, SmartPtr &sync) : _worker (worker) , _args (args) , _item (item) , _sync (sync) { } virtual XCamReturn run (); virtual void done (XCamReturn err); private: SmartPtr _worker; SmartPtr _args; WorkSize _item; SmartPtr _sync; }; XCamReturn WorkItem::run () { XCamReturn ret = _sync->get_error(); if (!xcam_ret_is_ok (ret)) return ret; ret = _worker->work_impl (_args, _item); if (!xcam_ret_is_ok (ret)) _sync->update_error (ret); return ret; } void WorkItem::done (XCamReturn err) { if (_sync->dec () == 0) { XCamReturn ret = _sync->get_error (); if (xcam_ret_is_ok (ret)) ret = err; _worker->all_items_done (_args, ret); } } SoftWorker::SoftWorker (const char *name, const SmartPtr &cb) : Worker (name, cb) , _work_unit (1, 1, 1) { } SoftWorker::~SoftWorker () { } bool SoftWorker::set_work_uint (uint32_t x, uint32_t y, uint32_t z) { XCAM_FAIL_RETURN ( ERROR, x && y && z, false, "SoftWorker(%s) set work unit failed(x:%d, y:%d, z:%d)", XCAM_STR (get_name ()), x, y, z); _work_unit.value[0] = x; _work_unit.value[1] = y; _work_unit.value[2] = z; return true; } bool SoftWorker::set_threads (const SmartPtr &threads) { XCAM_FAIL_RETURN ( ERROR, !_threads.ptr (), false, "SoftWorker(%s) set threads failed, it's already set before.", XCAM_STR (get_name ())); _threads = threads; return true; } XCamReturn SoftWorker::stop () { _threads->stop (); return XCAM_RETURN_NO_ERROR; } XCamReturn SoftWorker::work (const SmartPtr &args) { XCamReturn ret = XCAM_RETURN_NO_ERROR; const WorkSize &global = get_global_size (); const WorkSize &local = get_local_size (); XCAM_ASSERT (local.value[0] && local.value[1] && local.value[2]); XCAM_ASSERT (global.value[0] && global.value[1] && global.value[2]); WorkSize items; uint32_t max_items = 1; for (uint32_t i = 0; i < WORK_MAX_DIM; ++i) { items.value[i] = xcam_ceil (global.value[i], local.value[i]) / local.value[i]; max_items *= items.value[i]; } XCAM_FAIL_RETURN ( ERROR, max_items, XCAM_RETURN_ERROR_PARAM, "SoftWorker(%s) max item is zero. work failed.", XCAM_STR (get_name ())); if (max_items == 1) { ret = work_impl (args, WorkSize(0, 0, 0)); status_check (args, ret); return ret; } if (!_threads.ptr ()) { char thr_name [XCAM_MAX_STR_SIZE]; snprintf (thr_name, XCAM_MAX_STR_SIZE, "%s-thrs", XCAM_STR(get_name ())); SmartPtr threads = new ThreadPool (thr_name); XCAM_ASSERT (threads.ptr ()); _threads = threads; _threads->set_threads (max_items, max_items + 1); //extra thread to process all_items_done ret = _threads->start (); XCAM_FAIL_RETURN ( ERROR, xcam_ret_is_ok (ret), ret, "SoftWorker(%s) work failed when starting threads", XCAM_STR(get_name())); } SmartPtr sync = new ItemSynch (max_items); for (uint32_t z = 0; z < items.value[2]; ++z) for (uint32_t y = 0; y < items.value[1]; ++y) for (uint32_t x = 0; x < items.value[0]; ++x) { SmartPtr item = new WorkItem (this, args, WorkSize(x, y, z), sync); ret = _threads->queue (item); if (!xcam_ret_is_ok (ret)) { //consider half queued but half failed sync->update_error (ret); //status_check (args, ret); // need it here? XCAM_LOG_ERROR ( "SoftWorker(%s) queue work item(x:%d y: %d z:%d) failed", XCAM_STR(get_name()), x, y, z); return ret; } } return XCAM_RETURN_NO_ERROR; } void SoftWorker::all_items_done (const SmartPtr &args, XCamReturn error) { status_check (args, error); } WorkRange SoftWorker::get_range (const WorkSize &item) { WorkRange range; const WorkSize &global = get_global_size (); const WorkSize &local = get_local_size (); for (uint32_t i = 0; i < WORK_MAX_DIM; ++i) { range.pos[i] = item.value[i] * local.value[i]; XCAM_ASSERT (range.pos[i] < global.value[i]); if (range.pos[i] + local.value[i] > global.value[i]) range.pos_len[i] = global.value[i] - range.pos[i]; else range.pos_len[i] = local.value[i]; } return range; } XCamReturn SoftWorker::work_impl (const SmartPtr &args, const WorkSize &item) { WorkRange range = get_range (item); return work_range (args, range); } XCamReturn SoftWorker::work_range (const SmartPtr &args, const WorkRange &range) { XCamReturn ret = XCAM_RETURN_NO_ERROR; WorkSize unit; memcpy(unit.value, range.pos, sizeof (unit.value)); for (unit.value[2] = range.pos[2]; unit.value[2] < range.pos[2] + range.pos_len[2]; ++unit.value[2]) for (unit.value[1] = range.pos[1]; unit.value[1] < range.pos[1] + range.pos_len[1]; ++unit.value[1]) for (unit.value[0] = range.pos[0]; unit.value[0] < range.pos[0] + range.pos_len[0]; ++unit.value[0]) { ret = work_unit (args, unit); XCAM_FAIL_RETURN ( ERROR, xcam_ret_is_ok (ret), ret, "SoftWorker(%s) work on pixel(x:%d y: %d z:%d) failed", get_name (), unit.value[0], unit.value[1], unit.value[2]); } return ret; } XCamReturn SoftWorker::work_unit (const SmartPtr &, const WorkSize &) { XCAM_LOG_ERROR ("SoftWorker(%s) work_pixel was not derived. check code"); return XCAM_RETURN_ERROR_PARAM; } };