| /* |
| * 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 <[email protected]> |
| */ |
| |
| #include "soft_worker.h" |
| #include "thread_pool.h" |
| #include "xcam_mutex.h" |
| |
| namespace XCam { |
| |
| class ItemSynch { |
| private: |
| mutable std::atomic<uint32_t> _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<SoftWorker> &worker, |
| const SmartPtr<Worker::Arguments> &args, |
| const WorkSize &item, |
| SmartPtr<ItemSynch> &sync) |
| : _worker (worker) |
| , _args (args) |
| , _item (item) |
| , _sync (sync) |
| { |
| } |
| virtual XCamReturn run (); |
| virtual void done (XCamReturn err); |
| |
| |
| private: |
| SmartPtr<SoftWorker> _worker; |
| SmartPtr<Worker::Arguments> _args; |
| WorkSize _item; |
| SmartPtr<ItemSynch> _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<Callback> &cb) |
| : Worker (name, cb) |
| , _global (1, 1, 1) |
| , _local (1, 1, 1) |
| , _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<ThreadPool> &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; |
| } |
| |
| bool |
| SoftWorker::set_global_size (const WorkSize &size) |
| { |
| XCAM_FAIL_RETURN ( |
| ERROR, size.value[0] && size.value[1] && size.value[2], false, |
| "SoftWorker(%s) set global size(x:%d, y:%d, z:%d) failed.", |
| XCAM_STR (get_name ()), size.value[0], size.value[1], size.value[2]); |
| |
| _global = size; |
| return true; |
| } |
| |
| bool |
| SoftWorker::set_local_size (const WorkSize &size) |
| { |
| XCAM_FAIL_RETURN ( |
| ERROR, size.value[0] && size.value[1] && size.value[2], false, |
| "SoftWorker(%s) set local size(x:%d, y:%d, z:%d) failed.", |
| XCAM_STR (get_name ()), size.value[0], size.value[1], size.value[2]); |
| |
| _local = size; |
| return true; |
| } |
| |
| XCamReturn |
| SoftWorker::stop () |
| { |
| _threads->stop (); |
| return XCAM_RETURN_NO_ERROR; |
| } |
| |
| XCamReturn |
| SoftWorker::work (const SmartPtr<Worker::Arguments> &args) |
| { |
| XCamReturn ret = XCAM_RETURN_NO_ERROR; |
| |
| 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 < SOFT_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 ())); |
| _threads = new ThreadPool (thr_name); |
| XCAM_ASSERT (_threads.ptr ()); |
| _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<ItemSynch> 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<WorkItem> 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<Arguments> &args, XCamReturn error) |
| { |
| status_check (args, error); |
| } |
| |
| WorkRange |
| SoftWorker::get_range (const WorkSize &item) |
| { |
| WorkRange range; |
| for (uint32_t i = 0; i < SOFT_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<Arguments> &args, const WorkSize &item) |
| { |
| WorkRange range = get_range (item); |
| return work_range (args, range); |
| } |
| |
| XCamReturn |
| SoftWorker::work_range (const SmartPtr<Arguments> &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<Arguments> &, const WorkSize &) |
| { |
| XCAM_LOG_ERROR ("SoftWorker(%s) work_pixel was not derived. check code"); |
| return XCAM_RETURN_ERROR_PARAM; |
| } |
| |
| }; |