Coverage Report

Created: 2026-10-17 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/libde265/libde265/threads.cc
Line
Count
Source
1
/*
2
 * H.265 video codec.
3
 * Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
4
 *
5
 * This file is part of libde265.
6
 *
7
 * libde265 is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libde265 is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libde265.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "threads.h"
22
#include <assert.h>
23
#include <string.h>
24
25
#if defined(_MSC_VER) || defined(__MINGW32__)
26
# include <malloc.h>
27
#elif defined(HAVE_ALLOCA_H)
28
# include <alloca.h>
29
#endif
30
31
32
de265_progress_lock::de265_progress_lock()
33
13.5M
{
34
13.5M
  mProgress.store(0, std::memory_order_relaxed);
35
13.5M
}
36
37
de265_progress_lock::~de265_progress_lock()
38
13.5M
{
39
13.5M
}
40
41
void de265_progress_lock::wait_for_progress(int progress)
42
21
{
43
  // Fast path: acquire-load pairs with the release-store in set_progress()/
44
  // increase_progress() so that everything the signalling thread wrote before
45
  // reaching this progress value is visible once we observe it here.
46
21
  if (mProgress.load(std::memory_order_acquire) >= progress) {
47
21
    return;
48
21
  }
49
50
0
  std::unique_lock<std::mutex> lock(mutex);
51
0
  while (mProgress.load(std::memory_order_acquire) < progress) {
52
0
    cond.wait(lock);
53
0
  }
54
0
}
55
56
void de265_progress_lock::set_progress(int progress)
57
19.6M
{
58
19.6M
  std::unique_lock<std::mutex> lock(mutex);
59
60
19.6M
  if (progress > mProgress.load(std::memory_order_relaxed)) {
61
17.5M
    mProgress.store(progress, std::memory_order_release);
62
63
17.5M
    cond.notify_all();
64
17.5M
  }
65
19.6M
}
66
67
void de265_progress_lock::increase_progress(int progress)
68
80.4k
{
69
80.4k
  std::unique_lock<std::mutex> lock(mutex);
70
71
80.4k
  mProgress.store(mProgress.load(std::memory_order_relaxed) + progress,
72
80.4k
                  std::memory_order_release);
73
80.4k
  cond.notify_all();
74
80.4k
}
75
76
int  de265_progress_lock::get_progress() const
77
4.89M
{
78
4.89M
  return mProgress.load(std::memory_order_acquire);
79
4.89M
}
80
81
82
83
84
#include "libde265/decctx.h"
85
86
#if 0
87
const char* line="--------------------------------------------------";
88
void printblks(const thread_pool* pool)
89
{
90
  int w = pool->tasks[0].data.task_ctb.ctx->current_sps->PicWidthInCtbsY;
91
  int h = pool->tasks[0].data.task_ctb.ctx->current_sps->PicHeightInCtbsY;
92
93
  printf("active threads: %d  queue len: %d\n",pool->num_threads_working,pool->num_tasks);
94
95
  char *const p = (char *)alloca(w * h * sizeof(char));
96
  assert(p != nullptr);
97
  memset(p,' ',w*h);
98
99
  for (int i=0;i<pool->num_tasks;i++) {
100
    int b = 0; //pool->tasks[i].num_blockers;
101
    int x = pool->tasks[i].data.task_ctb.ctb_x;
102
    int y = pool->tasks[i].data.task_ctb.ctb_y;
103
    p[y*w+x] = b+'0';
104
  }
105
106
  for (int i=0;i<pool->num_threads_working;i++) {
107
    int x = pool->ctbx[i];
108
    int y = pool->ctby[i];
109
    p[y*w+x] = '*';
110
  }
111
112
  printf("+%s+\n",line+50-w);
113
  for (int y=0;y<h;y++)
114
    {
115
      printf("|");
116
      for (int x=0;x<w;x++)
117
        {
118
          printf("%c",p[x+y*w]);
119
        }
120
      printf("|\n");
121
    }
122
  printf("+%s+\n",line+50-w);
123
}
124
#endif
125
126
127
static void worker_thread(thread_pool* pool)
128
27.2k
{
129
2.09M
  while(true) {
130
131
2.09M
    thread_task* task = nullptr;
132
133
2.09M
    {
134
2.09M
      std::unique_lock<std::mutex> lock(pool->mutex);
135
136
      // wait until we can pick a task or until the pool has been stopped
137
138
2.13M
      for (;;) {
139
        // end waiting if thread-pool has been stopped or we have a task to execute
140
141
2.13M
        if (pool->stopped || pool->tasks.size()>0) {
142
2.09M
          break;
143
2.09M
        }
144
145
        //printf("going idle\n");
146
32.2k
        pool->cond_var.wait(lock);
147
32.2k
      }
148
149
      // if the pool was shut down, end the execution
150
151
2.09M
      if (pool->stopped) {
152
27.2k
        return;
153
27.2k
      }
154
155
156
      // get a task
157
158
2.07M
      task = pool->tasks.front();
159
2.07M
      pool->tasks.pop_front();
160
161
2.07M
      pool->num_threads_working++;
162
163
      //printblks(pool);
164
2.07M
    }
165
166
    // execute the task
167
168
0
    task->work();
169
170
    // end processing and check if this was the last task to be processed
171
172
    // TODO: the num_threads_working can probably be an atomic integer
173
2.07M
    std::unique_lock<std::mutex> lock(pool->mutex);
174
175
2.07M
    pool->num_threads_working--;
176
2.07M
  }
177
27.2k
}
178
179
180
de265_error thread_pool::start(int num_threads_to_start)
181
27.2k
{
182
27.2k
  de265_error err = DE265_OK;
183
184
  // limit number of threads to maximum
185
186
27.2k
  if (num_threads_to_start > MAX_THREADS) {
187
0
    num_threads_to_start = MAX_THREADS;
188
0
    err = DE265_WARNING_NUMBER_OF_THREADS_LIMITED_TO_MAXIMUM;
189
0
  }
190
191
27.2k
  num_threads = 0; // will be increased below
192
193
27.2k
  {
194
27.2k
    std::unique_lock<std::mutex> lock(mutex);
195
196
27.2k
    num_threads_working = 0;
197
27.2k
    stopped = false;
198
27.2k
  }
199
200
  // start worker threads
201
202
54.4k
  for (int i=0; i<num_threads_to_start; i++) {
203
27.2k
    thread[i] = std::thread(worker_thread, this);
204
27.2k
    num_threads++;
205
27.2k
  }
206
207
27.2k
  return err;
208
27.2k
}
209
210
211
void thread_pool::stop()
212
27.2k
{
213
27.2k
  {
214
27.2k
    std::unique_lock<std::mutex> lock(mutex);
215
27.2k
    stopped = true;
216
27.2k
  }
217
218
27.2k
  cond_var.notify_all();
219
220
54.4k
  for (int i=0;i<num_threads;i++) {
221
27.2k
    thread[i].join();
222
27.2k
  }
223
27.2k
}
224
225
226
void thread_pool::add_task(thread_task* task)
227
2.07M
{
228
2.07M
  std::unique_lock<std::mutex> lock(mutex);
229
230
2.07M
  if (!stopped) {
231
232
2.07M
    tasks.push_back(task);
233
234
    // wake up one thread
235
236
2.07M
    cond_var.notify_one();
237
2.07M
  }
238
2.07M
}