1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Output queue handling in multithreaded coding
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
16 /// Get the maximum number of buffers that may be allocated based
17 /// on the number of threads. For now this is twice the number of threads.
18 /// It's a compromise between RAM usage and keeping the worker threads busy
19 /// when buffers finish out of order.
20 #define GET_BUFS_LIMIT(threads) (2 * (threads))
24 lzma_outq_memusage(uint64_t buf_size_max
, uint32_t threads
)
26 // This is to ease integer overflow checking: We may allocate up to
27 // GET_BUFS_LIMIT(LZMA_THREADS_MAX) buffers and we need some extra
28 // memory for other data structures too (that's the /2).
30 // lzma_outq_prealloc_buf() will still accept bigger buffers than this.
32 = UINT64_MAX
/ GET_BUFS_LIMIT(LZMA_THREADS_MAX
) / 2;
34 if (threads
> LZMA_THREADS_MAX
|| buf_size_max
> limit
)
37 return GET_BUFS_LIMIT(threads
)
38 * lzma_outq_outbuf_memusage(buf_size_max
);
43 move_head_to_cache(lzma_outq
*outq
, const lzma_allocator
*allocator
)
45 assert(outq
->head
!= NULL
);
46 assert(outq
->tail
!= NULL
);
47 assert(outq
->bufs_in_use
> 0);
49 lzma_outbuf
*buf
= outq
->head
;
50 outq
->head
= buf
->next
;
51 if (outq
->head
== NULL
)
54 if (outq
->cache
!= NULL
&& outq
->cache
->allocated
!= buf
->allocated
)
55 lzma_outq_clear_cache(outq
, allocator
);
57 buf
->next
= outq
->cache
;
61 outq
->mem_in_use
-= lzma_outq_outbuf_memusage(buf
->allocated
);
68 free_one_cached_buffer(lzma_outq
*outq
, const lzma_allocator
*allocator
)
70 assert(outq
->cache
!= NULL
);
72 lzma_outbuf
*buf
= outq
->cache
;
73 outq
->cache
= buf
->next
;
75 --outq
->bufs_allocated
;
76 outq
->mem_allocated
-= lzma_outq_outbuf_memusage(buf
->allocated
);
78 lzma_free(buf
, allocator
);
84 lzma_outq_clear_cache(lzma_outq
*outq
, const lzma_allocator
*allocator
)
86 while (outq
->cache
!= NULL
)
87 free_one_cached_buffer(outq
, allocator
);
94 lzma_outq_clear_cache2(lzma_outq
*outq
, const lzma_allocator
*allocator
,
97 if (outq
->cache
== NULL
)
101 while (outq
->cache
->next
!= NULL
)
102 free_one_cached_buffer(outq
, allocator
);
104 // Free the last one only if its size doesn't equal to keep_size.
105 if (outq
->cache
->allocated
!= keep_size
)
106 free_one_cached_buffer(outq
, allocator
);
113 lzma_outq_init(lzma_outq
*outq
, const lzma_allocator
*allocator
,
116 if (threads
> LZMA_THREADS_MAX
)
117 return LZMA_OPTIONS_ERROR
;
119 const uint32_t bufs_limit
= GET_BUFS_LIMIT(threads
);
122 while (outq
->head
!= NULL
)
123 move_head_to_cache(outq
, allocator
);
125 // If new buf_limit is lower than the old one, we may need to free
126 // a few cached buffers.
127 while (bufs_limit
< outq
->bufs_allocated
)
128 free_one_cached_buffer(outq
, allocator
);
130 outq
->bufs_limit
= bufs_limit
;
138 lzma_outq_end(lzma_outq
*outq
, const lzma_allocator
*allocator
)
140 while (outq
->head
!= NULL
)
141 move_head_to_cache(outq
, allocator
);
143 lzma_outq_clear_cache(outq
, allocator
);
149 lzma_outq_prealloc_buf(lzma_outq
*outq
, const lzma_allocator
*allocator
,
152 // Caller must have checked it with lzma_outq_has_buf().
153 assert(outq
->bufs_in_use
< outq
->bufs_limit
);
155 // If there already is appropriately-sized buffer in the cache,
156 // we need to do nothing.
157 if (outq
->cache
!= NULL
&& outq
->cache
->allocated
== size
)
160 if (size
> SIZE_MAX
- sizeof(lzma_outbuf
))
161 return LZMA_MEM_ERROR
;
163 const size_t alloc_size
= lzma_outq_outbuf_memusage(size
);
165 // The cache may have buffers but their size is wrong.
166 lzma_outq_clear_cache(outq
, allocator
);
168 outq
->cache
= lzma_alloc(alloc_size
, allocator
);
169 if (outq
->cache
== NULL
)
170 return LZMA_MEM_ERROR
;
172 outq
->cache
->next
= NULL
;
173 outq
->cache
->allocated
= size
;
175 ++outq
->bufs_allocated
;
176 outq
->mem_allocated
+= alloc_size
;
183 lzma_outq_get_buf(lzma_outq
*outq
, void *worker
)
185 // Caller must have used lzma_outq_prealloc_buf() to ensure these.
186 assert(outq
->bufs_in_use
< outq
->bufs_limit
);
187 assert(outq
->bufs_in_use
< outq
->bufs_allocated
);
188 assert(outq
->cache
!= NULL
);
190 lzma_outbuf
*buf
= outq
->cache
;
191 outq
->cache
= buf
->next
;
194 if (outq
->tail
!= NULL
) {
195 assert(outq
->head
!= NULL
);
196 outq
->tail
->next
= buf
;
198 assert(outq
->head
== NULL
);
204 buf
->worker
= worker
;
205 buf
->finished
= false;
206 buf
->finish_ret
= LZMA_STREAM_END
;
208 buf
->decoder_in_pos
= 0;
210 buf
->unpadded_size
= 0;
211 buf
->uncompressed_size
= 0;
214 outq
->mem_in_use
+= lzma_outq_outbuf_memusage(buf
->allocated
);
221 lzma_outq_is_readable(const lzma_outq
*outq
)
223 if (outq
->head
== NULL
)
226 return outq
->read_pos
< outq
->head
->pos
|| outq
->head
->finished
;
231 lzma_outq_read(lzma_outq
*restrict outq
,
232 const lzma_allocator
*restrict allocator
,
233 uint8_t *restrict out
, size_t *restrict out_pos
,
235 lzma_vli
*restrict unpadded_size
,
236 lzma_vli
*restrict uncompressed_size
)
238 // There must be at least one buffer from which to read.
239 if (outq
->bufs_in_use
== 0)
243 lzma_outbuf
*buf
= outq
->head
;
245 // Copy from the buffer to output.
247 // FIXME? In threaded decoder it may be bad to do this copy while
248 // the mutex is being held.
249 lzma_bufcpy(buf
->buf
, &outq
->read_pos
, buf
->pos
,
250 out
, out_pos
, out_size
);
252 // Return if we didn't get all the data from the buffer.
253 if (!buf
->finished
|| outq
->read_pos
< buf
->pos
)
256 // The buffer was finished. Tell the caller its size information.
257 if (unpadded_size
!= NULL
)
258 *unpadded_size
= buf
->unpadded_size
;
260 if (uncompressed_size
!= NULL
)
261 *uncompressed_size
= buf
->uncompressed_size
;
263 // Remember the return value.
264 const lzma_ret finish_ret
= buf
->finish_ret
;
266 // Free this buffer for further use.
267 move_head_to_cache(outq
, allocator
);
275 lzma_outq_enable_partial_output(lzma_outq
*outq
,
276 void (*enable_partial_output
)(void *worker
))
278 if (outq
->head
!= NULL
&& !outq
->head
->finished
279 && outq
->head
->worker
!= NULL
) {
280 enable_partial_output(outq
->head
->worker
);
282 // Set it to NULL since calling it twice is pointless.
283 outq
->head
->worker
= NULL
;