1/*
2 * soft_worker.cpp - soft worker implementation
3 *
4 *  Copyright (c) 2017 Intel Corporation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * Author: Wind Yuan <feng.yuan@intel.com>
19 */
20
21#include "soft_worker.h"
22#include "thread_pool.h"
23#include "xcam_mutex.h"
24
25namespace XCam {
26
27class ItemSynch {
28private:
29    mutable std::atomic<uint32_t>  _remain_items;
30    Mutex                          _mutex;
31    XCamReturn                     _error;
32
33public:
34    ItemSynch (uint32_t items)
35        : _remain_items(items), _error (XCAM_RETURN_NO_ERROR)
36    {}
37    void update_error (XCamReturn err) {
38        SmartLock locker(_mutex);
39        _error = err;
40    }
41    XCamReturn get_error () {
42        SmartLock locker(_mutex);
43        return _error;
44    }
45    uint32_t dec() {
46        return --_remain_items;
47    }
48
49private:
50    XCAM_DEAD_COPY (ItemSynch);
51};
52
53class WorkItem
54    : public ThreadPool::UserData
55{
56public:
57    WorkItem (
58        const SmartPtr<SoftWorker> &worker,
59        const SmartPtr<Worker::Arguments> &args,
60        const WorkSize &item,
61        SmartPtr<ItemSynch> &sync)
62        : _worker (worker)
63        , _args (args)
64        , _item (item)
65        , _sync (sync)
66    {
67    }
68    virtual XCamReturn run ();
69    virtual void done (XCamReturn err);
70
71
72private:
73    SmartPtr<SoftWorker>         _worker;
74    SmartPtr<Worker::Arguments>  _args;
75    WorkSize                     _item;
76    SmartPtr<ItemSynch>          _sync;
77};
78
79XCamReturn
80WorkItem::run ()
81{
82    XCamReturn ret = _sync->get_error();
83    if (!xcam_ret_is_ok (ret))
84        return ret;
85
86    ret = _worker->work_impl (_args, _item);
87    if (!xcam_ret_is_ok (ret))
88        _sync->update_error (ret);
89
90    return ret;
91}
92
93void
94WorkItem::done (XCamReturn err)
95{
96    if (_sync->dec () == 0) {
97        XCamReturn ret = _sync->get_error ();
98        if (xcam_ret_is_ok (ret))
99            ret = err;
100        _worker->all_items_done (_args, ret);
101    }
102}
103
104SoftWorker::SoftWorker (const char *name, const SmartPtr<Callback> &cb)
105    : Worker (name, cb)
106    , _global (1, 1, 1)
107    , _local (1, 1, 1)
108    , _work_unit (1, 1, 1)
109{
110}
111
112SoftWorker::~SoftWorker ()
113{
114}
115
116bool
117SoftWorker::set_work_uint (uint32_t x, uint32_t y, uint32_t z)
118{
119    XCAM_FAIL_RETURN (
120        ERROR, x && y && z, false,
121        "SoftWorker(%s) set work unit failed(x:%d, y:%d, z:%d)",
122        XCAM_STR (get_name ()), x, y, z);
123    _work_unit.value[0] = x;
124    _work_unit.value[1] = y;
125    _work_unit.value[2] = z;
126    return true;
127}
128
129bool
130SoftWorker::set_threads (const SmartPtr<ThreadPool> &threads)
131{
132    XCAM_FAIL_RETURN (
133        ERROR, !_threads.ptr (), false,
134        "SoftWorker(%s) set threads failed, it's already set before.", XCAM_STR (get_name ()));
135    _threads = threads;
136    return true;
137}
138
139bool
140SoftWorker::set_global_size (const WorkSize &size)
141{
142    XCAM_FAIL_RETURN (
143        ERROR, size.value[0] && size.value[1] && size.value[2], false,
144        "SoftWorker(%s) set global size(x:%d, y:%d, z:%d) failed.",
145        XCAM_STR (get_name ()), size.value[0], size.value[1], size.value[2]);
146
147    _global = size;
148    return true;
149}
150
151bool
152SoftWorker::set_local_size (const WorkSize &size)
153{
154    XCAM_FAIL_RETURN (
155        ERROR, size.value[0] && size.value[1] && size.value[2], false,
156        "SoftWorker(%s) set local size(x:%d, y:%d, z:%d) failed.",
157        XCAM_STR (get_name ()), size.value[0], size.value[1], size.value[2]);
158
159    _local = size;
160    return true;
161}
162
163XCamReturn
164SoftWorker::stop ()
165{
166    _threads->stop ();
167    return XCAM_RETURN_NO_ERROR;
168}
169
170XCamReturn
171SoftWorker::work (const SmartPtr<Worker::Arguments> &args)
172{
173    XCamReturn ret = XCAM_RETURN_NO_ERROR;
174
175    XCAM_ASSERT (_local.value[0] * _local.value[1] * _local.value[2]);
176    XCAM_ASSERT (_global.value[0] * _global.value[1] * _global.value[2]);
177
178    WorkSize items;
179    uint32_t max_items = 1;
180
181    for (uint32_t i = 0; i < SOFT_MAX_DIM; ++i) {
182        items.value[i] = xcam_ceil (_global.value[i],  _local.value[i]) / _local.value[i];
183        max_items *= items.value[i];
184    }
185
186    XCAM_FAIL_RETURN (
187        ERROR, max_items, XCAM_RETURN_ERROR_PARAM,
188        "SoftWorker(%s) max item is zero. work failed.", XCAM_STR (get_name ()));
189
190    if (max_items == 1) {
191        ret = work_impl (args, WorkSize(0, 0, 0));
192        status_check (args, ret);
193        return ret;
194    }
195
196    if (!_threads.ptr ()) {
197        char thr_name [XCAM_MAX_STR_SIZE];
198        snprintf (thr_name, XCAM_MAX_STR_SIZE, "%s-thrs", XCAM_STR(get_name ()));
199        _threads = new ThreadPool (thr_name);
200        XCAM_ASSERT (_threads.ptr ());
201        _threads->set_threads (max_items, max_items + 1); //extra thread to process all_items_done
202        ret = _threads->start ();
203        XCAM_FAIL_RETURN (
204            ERROR, xcam_ret_is_ok (ret), ret,
205            "SoftWorker(%s) work failed when starting threads", XCAM_STR(get_name()));
206    }
207
208    SmartPtr<ItemSynch> sync = new ItemSynch (max_items);
209    for (uint32_t z = 0; z < items.value[2]; ++z)
210        for (uint32_t y = 0; y < items.value[1]; ++y)
211            for (uint32_t x = 0; x < items.value[0]; ++x)
212            {
213                SmartPtr<WorkItem> item = new WorkItem (this, args, WorkSize(x, y, z), sync);
214                ret = _threads->queue (item);
215                if (!xcam_ret_is_ok (ret)) {
216                    //consider half queued but half failed
217                    sync->update_error (ret);
218                    //status_check (args, ret); // need it here?
219                    XCAM_LOG_ERROR (
220                        "SoftWorker(%s) queue work item(x:%d y: %d z:%d) failed",
221                        XCAM_STR(get_name()), x, y, z);
222                    return ret;
223                }
224            }
225
226    return XCAM_RETURN_NO_ERROR;
227}
228
229void
230SoftWorker::all_items_done (const SmartPtr<Arguments> &args, XCamReturn error)
231{
232    status_check (args, error);
233}
234
235WorkRange
236SoftWorker::get_range (const WorkSize &item)
237{
238    WorkRange range;
239    for (uint32_t i = 0; i < SOFT_MAX_DIM; ++i) {
240        range.pos[i] = item.value[i] * _local.value[i];
241        XCAM_ASSERT (range.pos[i] < _global.value[i]);
242        if (range.pos[i] + _local.value[i] > _global.value[i])
243            range.pos_len[i] = _global.value[i] - range.pos[i];
244        else
245            range.pos_len[i] = _local.value[i];
246    }
247    return range;
248}
249
250XCamReturn
251SoftWorker::work_impl (const SmartPtr<Arguments> &args, const WorkSize &item)
252{
253    WorkRange range = get_range (item);
254    return work_range (args, range);
255}
256
257XCamReturn
258SoftWorker::work_range (const SmartPtr<Arguments> &args, const WorkRange &range)
259{
260    XCamReturn ret = XCAM_RETURN_NO_ERROR;
261    WorkSize unit;
262    memcpy(unit.value, range.pos, sizeof (unit.value));
263
264    for (unit.value[2] = range.pos[2]; unit.value[2] < range.pos[2] + range.pos_len[2]; ++unit.value[2])
265        for (unit.value[1] = range.pos[1]; unit.value[1] < range.pos[1] + range.pos_len[1]; ++unit.value[1])
266            for (unit.value[0] = range.pos[0]; unit.value[0] < range.pos[0] + range.pos_len[0]; ++unit.value[0]) {
267                ret = work_unit (args, unit);
268                XCAM_FAIL_RETURN (
269                    ERROR, xcam_ret_is_ok (ret), ret,
270                    "SoftWorker(%s) work on pixel(x:%d y: %d z:%d) failed",
271                    get_name (), unit.value[0], unit.value[1], unit.value[2]);
272            }
273
274    return ret;
275}
276
277XCamReturn
278SoftWorker::work_unit (const SmartPtr<Arguments> &, const WorkSize &)
279{
280    XCAM_LOG_ERROR ("SoftWorker(%s) work_pixel was not derived. check code");
281    return XCAM_RETURN_ERROR_PARAM;
282}
283
284};
285