657 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			657 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /* MtCoder.c -- Multi-thread Coder
 | ||
|  | 2017-07-17 : Igor Pavlov : Public domain */ | ||
|  | 
 | ||
|  | #include "Precomp.h"
 | ||
|  | 
 | ||
|  | #include "MtCoder.h"
 | ||
|  | 
 | ||
|  | static void MtProgress_Init(CMtProgress *p, ICompressProgress *progress) | ||
|  | { | ||
|  |   unsigned i; | ||
|  | 
 | ||
|  |   p->progress = progress; | ||
|  |   p->res = SZ_OK; | ||
|  |   p->totalInSize = 0; | ||
|  |   p->totalOutSize = 0; | ||
|  |    | ||
|  |   for (i = 0; i < MTCODER__THREADS_MAX; i++) | ||
|  |   { | ||
|  |     CMtProgressSizes *pair = &p->sizes[i]; | ||
|  |     pair->inSize = 0; | ||
|  |     pair->outSize = 0; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void MtProgress_Reinit(CMtProgress *p, unsigned index) | ||
|  | { | ||
|  |   CMtProgressSizes *pair = &p->sizes[index]; | ||
|  |   pair->inSize = 0; | ||
|  |   pair->outSize = 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | #define UPDATE_PROGRESS(size, prev, total) \
 | ||
|  |   if (size != (UInt64)(Int64)-1) { total += size - prev; prev = size; } | ||
|  | 
 | ||
|  | SRes MtProgress_Set(CMtProgress *p, unsigned index, UInt64 inSize, UInt64 outSize) | ||
|  | { | ||
|  |   SRes res; | ||
|  |   CMtProgressSizes *pair; | ||
|  |    | ||
|  |   CriticalSection_Enter(&p->cs); | ||
|  |    | ||
|  |   pair = &p->sizes[index]; | ||
|  |   UPDATE_PROGRESS(inSize, pair->inSize, p->totalInSize) | ||
|  |   UPDATE_PROGRESS(outSize, pair->outSize, p->totalOutSize) | ||
|  |   if (p->res == SZ_OK && p->progress) | ||
|  |   { | ||
|  |     if (ICompressProgress_Progress(p->progress, p->totalInSize, p->totalOutSize) != SZ_OK) | ||
|  |       p->res = SZ_ERROR_PROGRESS; | ||
|  |   } | ||
|  |   res = p->res; | ||
|  |    | ||
|  |   CriticalSection_Leave(&p->cs); | ||
|  |    | ||
|  |   return res; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static SRes MtProgress_GetError(CMtProgress *p) | ||
|  | { | ||
|  |   SRes res; | ||
|  |   CriticalSection_Enter(&p->cs); | ||
|  |   res = p->res; | ||
|  |   CriticalSection_Leave(&p->cs); | ||
|  |   return res; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void MtProgress_SetError(CMtProgress *p, SRes res) | ||
|  | { | ||
|  |   CriticalSection_Enter(&p->cs); | ||
|  |   if (p->res == SZ_OK) | ||
|  |     p->res = res; | ||
|  |   CriticalSection_Leave(&p->cs); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static SRes MtProgressThunk_Progress(const ICompressProgress *pp, UInt64 inSize, UInt64 outSize) | ||
|  | { | ||
|  |   CMtProgressThunk *p = CONTAINER_FROM_VTBL(pp, CMtProgressThunk, vt); | ||
|  |   return MtProgress_Set(p->mtProgress, p->index, inSize, outSize); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void MtProgressThunk_CreateVTable(CMtProgressThunk *p) | ||
|  | { | ||
|  |   p->vt.Progress = MtProgressThunk_Progress; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; }
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static WRes ArEvent_OptCreate_And_Reset(CEvent *p) | ||
|  | { | ||
|  |   if (Event_IsCreated(p)) | ||
|  |     return Event_Reset(p); | ||
|  |   return AutoResetEvent_CreateNotSignaled(p); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE ThreadFunc(void *pp); | ||
|  | 
 | ||
|  | 
 | ||
|  | static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t) | ||
|  | { | ||
|  |   WRes wres = ArEvent_OptCreate_And_Reset(&t->startEvent); | ||
|  |   if (wres == 0) | ||
|  |   { | ||
|  |     t->stop = False; | ||
|  |     if (!Thread_WasCreated(&t->thread)) | ||
|  |       wres = Thread_Create(&t->thread, ThreadFunc, t); | ||
|  |     if (wres == 0) | ||
|  |       wres = Event_Set(&t->startEvent); | ||
|  |   } | ||
|  |   if (wres == 0) | ||
|  |     return SZ_OK; | ||
|  |   return MY_SRes_HRESULT_FROM_WRes(wres); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void MtCoderThread_Destruct(CMtCoderThread *t) | ||
|  | { | ||
|  |   if (Thread_WasCreated(&t->thread)) | ||
|  |   { | ||
|  |     t->stop = 1; | ||
|  |     Event_Set(&t->startEvent); | ||
|  |     Thread_Wait(&t->thread); | ||
|  |     Thread_Close(&t->thread); | ||
|  |   } | ||
|  | 
 | ||
|  |   Event_Close(&t->startEvent); | ||
|  | 
 | ||
|  |   if (t->inBuf) | ||
|  |   { | ||
|  |     ISzAlloc_Free(t->mtCoder->allocBig, t->inBuf); | ||
|  |     t->inBuf = NULL; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static SRes FullRead(ISeqInStream *stream, Byte *data, size_t *processedSize) | ||
|  | { | ||
|  |   size_t size = *processedSize; | ||
|  |   *processedSize = 0; | ||
|  |   while (size != 0) | ||
|  |   { | ||
|  |     size_t cur = size; | ||
|  |     SRes res = ISeqInStream_Read(stream, data, &cur); | ||
|  |     *processedSize += cur; | ||
|  |     data += cur; | ||
|  |     size -= cur; | ||
|  |     RINOK(res); | ||
|  |     if (cur == 0) | ||
|  |       return SZ_OK; | ||
|  |   } | ||
|  |   return SZ_OK; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /*
 | ||
|  |   ThreadFunc2() returns: | ||
|  |   SZ_OK           - in all normal cases (even for stream error or memory allocation error) | ||
|  |   SZ_ERROR_THREAD - in case of failure in system synch function | ||
|  | */ | ||
|  | 
 | ||
|  | static SRes ThreadFunc2(CMtCoderThread *t) | ||
|  | { | ||
|  |   CMtCoder *mtc = t->mtCoder; | ||
|  | 
 | ||
|  |   for (;;) | ||
|  |   { | ||
|  |     unsigned bi; | ||
|  |     SRes res; | ||
|  |     SRes res2; | ||
|  |     Bool finished; | ||
|  |     unsigned bufIndex; | ||
|  |     size_t size; | ||
|  |     const Byte *inData; | ||
|  |     UInt64 readProcessed = 0; | ||
|  |      | ||
|  |     RINOK_THREAD(Event_Wait(&mtc->readEvent)) | ||
|  | 
 | ||
|  |     /* after Event_Wait(&mtc->readEvent) we must call Event_Set(&mtc->readEvent) in any case to unlock another threads */ | ||
|  | 
 | ||
|  |     if (mtc->stopReading) | ||
|  |     { | ||
|  |       return Event_Set(&mtc->readEvent) == 0 ? SZ_OK : SZ_ERROR_THREAD; | ||
|  |     } | ||
|  | 
 | ||
|  |     res = MtProgress_GetError(&mtc->mtProgress); | ||
|  |      | ||
|  |     size = 0; | ||
|  |     inData = NULL; | ||
|  |     finished = True; | ||
|  | 
 | ||
|  |     if (res == SZ_OK) | ||
|  |     { | ||
|  |       size = mtc->blockSize; | ||
|  |       if (mtc->inStream) | ||
|  |       { | ||
|  |         if (!t->inBuf) | ||
|  |         { | ||
|  |           t->inBuf = (Byte *)ISzAlloc_Alloc(mtc->allocBig, mtc->blockSize); | ||
|  |           if (!t->inBuf) | ||
|  |             res = SZ_ERROR_MEM; | ||
|  |         } | ||
|  |         if (res == SZ_OK) | ||
|  |         { | ||
|  |           res = FullRead(mtc->inStream, t->inBuf, &size); | ||
|  |           readProcessed = mtc->readProcessed + size; | ||
|  |           mtc->readProcessed = readProcessed; | ||
|  |         } | ||
|  |         if (res != SZ_OK) | ||
|  |         { | ||
|  |           mtc->readRes = res; | ||
|  |           /* after reading error - we can stop encoding of previous blocks */ | ||
|  |           MtProgress_SetError(&mtc->mtProgress, res); | ||
|  |         } | ||
|  |         else | ||
|  |           finished = (size != mtc->blockSize); | ||
|  |       } | ||
|  |       else | ||
|  |       { | ||
|  |         size_t rem; | ||
|  |         readProcessed = mtc->readProcessed; | ||
|  |         rem = mtc->inDataSize - (size_t)readProcessed; | ||
|  |         if (size > rem) | ||
|  |           size = rem; | ||
|  |         inData = mtc->inData + (size_t)readProcessed; | ||
|  |         readProcessed += size; | ||
|  |         mtc->readProcessed = readProcessed; | ||
|  |         finished = (mtc->inDataSize == (size_t)readProcessed); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     /* we must get some block from blocksSemaphore before Event_Set(&mtc->readEvent) */ | ||
|  | 
 | ||
|  |     res2 = SZ_OK; | ||
|  | 
 | ||
|  |     if (Semaphore_Wait(&mtc->blocksSemaphore) != 0) | ||
|  |     { | ||
|  |       res2 = SZ_ERROR_THREAD; | ||
|  |       if (res == SZ_OK) | ||
|  |       { | ||
|  |         res = res2; | ||
|  |         // MtProgress_SetError(&mtc->mtProgress, res);
 | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     bi = mtc->blockIndex; | ||
|  | 
 | ||
|  |     if (++mtc->blockIndex >= mtc->numBlocksMax) | ||
|  |       mtc->blockIndex = 0; | ||
|  | 
 | ||
|  |     bufIndex = (unsigned)(int)-1; | ||
|  | 
 | ||
|  |     if (res == SZ_OK) | ||
|  |       res = MtProgress_GetError(&mtc->mtProgress); | ||
|  | 
 | ||
|  |     if (res != SZ_OK) | ||
|  |       finished = True; | ||
|  | 
 | ||
|  |     if (!finished) | ||
|  |     { | ||
|  |       if (mtc->numStartedThreads < mtc->numStartedThreadsLimit | ||
|  |           && mtc->expectedDataSize != readProcessed) | ||
|  |       { | ||
|  |         res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads]); | ||
|  |         if (res == SZ_OK) | ||
|  |           mtc->numStartedThreads++; | ||
|  |         else | ||
|  |         { | ||
|  |           MtProgress_SetError(&mtc->mtProgress, res); | ||
|  |           finished = True; | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (finished) | ||
|  |       mtc->stopReading = True; | ||
|  | 
 | ||
|  |     RINOK_THREAD(Event_Set(&mtc->readEvent)) | ||
|  | 
 | ||
|  |     if (res2 != SZ_OK) | ||
|  |       return res2; | ||
|  | 
 | ||
|  |     if (res == SZ_OK) | ||
|  |     { | ||
|  |       CriticalSection_Enter(&mtc->cs); | ||
|  |       bufIndex = mtc->freeBlockHead; | ||
|  |       mtc->freeBlockHead = mtc->freeBlockList[bufIndex]; | ||
|  |       CriticalSection_Leave(&mtc->cs); | ||
|  |        | ||
|  |       res = mtc->mtCallback->Code(mtc->mtCallbackObject, t->index, bufIndex, | ||
|  |           mtc->inStream ? t->inBuf : inData, size, finished); | ||
|  |        | ||
|  |       MtProgress_Reinit(&mtc->mtProgress, t->index); | ||
|  | 
 | ||
|  |       if (res != SZ_OK) | ||
|  |         MtProgress_SetError(&mtc->mtProgress, res); | ||
|  |     } | ||
|  | 
 | ||
|  |     { | ||
|  |       CMtCoderBlock *block = &mtc->blocks[bi]; | ||
|  |       block->res = res; | ||
|  |       block->bufIndex = bufIndex; | ||
|  |       block->finished = finished; | ||
|  |     } | ||
|  |      | ||
|  |     #ifdef MTCODER__USE_WRITE_THREAD
 | ||
|  |       RINOK_THREAD(Event_Set(&mtc->writeEvents[bi])) | ||
|  |     #else
 | ||
|  |     { | ||
|  |       unsigned wi; | ||
|  |       { | ||
|  |         CriticalSection_Enter(&mtc->cs); | ||
|  |         wi = mtc->writeIndex; | ||
|  |         if (wi == bi) | ||
|  |           mtc->writeIndex = (unsigned)(int)-1; | ||
|  |         else | ||
|  |           mtc->ReadyBlocks[bi] = True; | ||
|  |         CriticalSection_Leave(&mtc->cs); | ||
|  |       } | ||
|  | 
 | ||
|  |       if (wi != bi) | ||
|  |       { | ||
|  |         if (res != SZ_OK || finished) | ||
|  |           return 0; | ||
|  |         continue; | ||
|  |       } | ||
|  | 
 | ||
|  |       if (mtc->writeRes != SZ_OK) | ||
|  |         res = mtc->writeRes; | ||
|  | 
 | ||
|  |       for (;;) | ||
|  |       { | ||
|  |         if (res == SZ_OK && bufIndex != (unsigned)(int)-1) | ||
|  |         { | ||
|  |           res = mtc->mtCallback->Write(mtc->mtCallbackObject, bufIndex); | ||
|  |           if (res != SZ_OK) | ||
|  |           { | ||
|  |             mtc->writeRes = res; | ||
|  |             MtProgress_SetError(&mtc->mtProgress, res); | ||
|  |           } | ||
|  |         } | ||
|  | 
 | ||
|  |         if (++wi >= mtc->numBlocksMax) | ||
|  |           wi = 0; | ||
|  |         { | ||
|  |           Bool isReady; | ||
|  | 
 | ||
|  |           CriticalSection_Enter(&mtc->cs); | ||
|  |            | ||
|  |           if (bufIndex != (unsigned)(int)-1) | ||
|  |           { | ||
|  |             mtc->freeBlockList[bufIndex] = mtc->freeBlockHead; | ||
|  |             mtc->freeBlockHead = bufIndex; | ||
|  |           } | ||
|  |            | ||
|  |           isReady = mtc->ReadyBlocks[wi]; | ||
|  |            | ||
|  |           if (isReady) | ||
|  |             mtc->ReadyBlocks[wi] = False; | ||
|  |           else | ||
|  |             mtc->writeIndex = wi; | ||
|  |            | ||
|  |           CriticalSection_Leave(&mtc->cs); | ||
|  | 
 | ||
|  |           RINOK_THREAD(Semaphore_Release1(&mtc->blocksSemaphore)) | ||
|  | 
 | ||
|  |           if (!isReady) | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |         { | ||
|  |           CMtCoderBlock *block = &mtc->blocks[wi]; | ||
|  |           if (res == SZ_OK && block->res != SZ_OK) | ||
|  |             res = block->res; | ||
|  |           bufIndex = block->bufIndex; | ||
|  |           finished = block->finished; | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |     #endif
 | ||
|  |        | ||
|  |     if (finished || res != SZ_OK) | ||
|  |       return 0; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE ThreadFunc(void *pp) | ||
|  | { | ||
|  |   CMtCoderThread *t = (CMtCoderThread *)pp; | ||
|  |   for (;;) | ||
|  |   { | ||
|  |     if (Event_Wait(&t->startEvent) != 0) | ||
|  |       return SZ_ERROR_THREAD; | ||
|  |     if (t->stop) | ||
|  |       return 0; | ||
|  |     { | ||
|  |       SRes res = ThreadFunc2(t); | ||
|  |       CMtCoder *mtc = t->mtCoder; | ||
|  |       if (res != SZ_OK) | ||
|  |       { | ||
|  |         MtProgress_SetError(&mtc->mtProgress, res); | ||
|  |       } | ||
|  |        | ||
|  |       #ifndef MTCODER__USE_WRITE_THREAD
 | ||
|  |       { | ||
|  |         unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads); | ||
|  |         if (numFinished == mtc->numStartedThreads) | ||
|  |           if (Event_Set(&mtc->finishedEvent) != 0) | ||
|  |             return SZ_ERROR_THREAD; | ||
|  |       } | ||
|  |       #endif
 | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | void MtCoder_Construct(CMtCoder *p) | ||
|  | { | ||
|  |   unsigned i; | ||
|  |    | ||
|  |   p->blockSize = 0; | ||
|  |   p->numThreadsMax = 0; | ||
|  |   p->expectedDataSize = (UInt64)(Int64)-1; | ||
|  | 
 | ||
|  |   p->inStream = NULL; | ||
|  |   p->inData = NULL; | ||
|  |   p->inDataSize = 0; | ||
|  | 
 | ||
|  |   p->progress = NULL; | ||
|  |   p->allocBig = NULL; | ||
|  | 
 | ||
|  |   p->mtCallback = NULL; | ||
|  |   p->mtCallbackObject = NULL; | ||
|  | 
 | ||
|  |   p->allocatedBufsSize = 0; | ||
|  | 
 | ||
|  |   Event_Construct(&p->readEvent); | ||
|  |   Semaphore_Construct(&p->blocksSemaphore); | ||
|  | 
 | ||
|  |   for (i = 0; i < MTCODER__THREADS_MAX; i++) | ||
|  |   { | ||
|  |     CMtCoderThread *t = &p->threads[i]; | ||
|  |     t->mtCoder = p; | ||
|  |     t->index = i; | ||
|  |     t->inBuf = NULL; | ||
|  |     t->stop = False; | ||
|  |     Event_Construct(&t->startEvent); | ||
|  |     Thread_Construct(&t->thread); | ||
|  |   } | ||
|  | 
 | ||
|  |   #ifdef MTCODER__USE_WRITE_THREAD
 | ||
|  |     for (i = 0; i < MTCODER__BLOCKS_MAX; i++) | ||
|  |       Event_Construct(&p->writeEvents[i]); | ||
|  |   #else
 | ||
|  |     Event_Construct(&p->finishedEvent); | ||
|  |   #endif
 | ||
|  | 
 | ||
|  |   CriticalSection_Init(&p->cs); | ||
|  |   CriticalSection_Init(&p->mtProgress.cs); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static void MtCoder_Free(CMtCoder *p) | ||
|  | { | ||
|  |   unsigned i; | ||
|  | 
 | ||
|  |   /*
 | ||
|  |   p->stopReading = True; | ||
|  |   if (Event_IsCreated(&p->readEvent)) | ||
|  |     Event_Set(&p->readEvent); | ||
|  |   */ | ||
|  | 
 | ||
|  |   for (i = 0; i < MTCODER__THREADS_MAX; i++) | ||
|  |     MtCoderThread_Destruct(&p->threads[i]); | ||
|  | 
 | ||
|  |   Event_Close(&p->readEvent); | ||
|  |   Semaphore_Close(&p->blocksSemaphore); | ||
|  | 
 | ||
|  |   #ifdef MTCODER__USE_WRITE_THREAD
 | ||
|  |     for (i = 0; i < MTCODER__BLOCKS_MAX; i++) | ||
|  |       Event_Close(&p->writeEvents[i]); | ||
|  |   #else
 | ||
|  |     Event_Close(&p->finishedEvent); | ||
|  |   #endif
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | void MtCoder_Destruct(CMtCoder *p) | ||
|  | { | ||
|  |   MtCoder_Free(p); | ||
|  | 
 | ||
|  |   CriticalSection_Delete(&p->cs); | ||
|  |   CriticalSection_Delete(&p->mtProgress.cs); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | SRes MtCoder_Code(CMtCoder *p) | ||
|  | { | ||
|  |   unsigned numThreads = p->numThreadsMax; | ||
|  |   unsigned numBlocksMax; | ||
|  |   unsigned i; | ||
|  |   SRes res = SZ_OK; | ||
|  | 
 | ||
|  |   if (numThreads > MTCODER__THREADS_MAX) | ||
|  |     numThreads = MTCODER__THREADS_MAX; | ||
|  |   numBlocksMax = MTCODER__GET_NUM_BLOCKS_FROM_THREADS(numThreads); | ||
|  |    | ||
|  |   if (p->blockSize < ((UInt32)1 << 26)) numBlocksMax++; | ||
|  |   if (p->blockSize < ((UInt32)1 << 24)) numBlocksMax++; | ||
|  |   if (p->blockSize < ((UInt32)1 << 22)) numBlocksMax++; | ||
|  | 
 | ||
|  |   if (numBlocksMax > MTCODER__BLOCKS_MAX) | ||
|  |     numBlocksMax = MTCODER__BLOCKS_MAX; | ||
|  | 
 | ||
|  |   if (p->blockSize != p->allocatedBufsSize) | ||
|  |   { | ||
|  |     for (i = 0; i < MTCODER__THREADS_MAX; i++) | ||
|  |     { | ||
|  |       CMtCoderThread *t = &p->threads[i]; | ||
|  |       if (t->inBuf) | ||
|  |       { | ||
|  |         ISzAlloc_Free(p->allocBig, t->inBuf); | ||
|  |         t->inBuf = NULL; | ||
|  |       } | ||
|  |     } | ||
|  |     p->allocatedBufsSize = p->blockSize; | ||
|  |   } | ||
|  | 
 | ||
|  |   p->readRes = SZ_OK; | ||
|  | 
 | ||
|  |   MtProgress_Init(&p->mtProgress, p->progress); | ||
|  | 
 | ||
|  |   #ifdef MTCODER__USE_WRITE_THREAD
 | ||
|  |     for (i = 0; i < numBlocksMax; i++) | ||
|  |     { | ||
|  |       RINOK_THREAD(ArEvent_OptCreate_And_Reset(&p->writeEvents[i])); | ||
|  |     } | ||
|  |   #else
 | ||
|  |     RINOK_THREAD(ArEvent_OptCreate_And_Reset(&p->finishedEvent)); | ||
|  |   #endif
 | ||
|  | 
 | ||
|  |   { | ||
|  |     RINOK_THREAD(ArEvent_OptCreate_And_Reset(&p->readEvent)); | ||
|  | 
 | ||
|  |     if (Semaphore_IsCreated(&p->blocksSemaphore)) | ||
|  |     { | ||
|  |       RINOK_THREAD(Semaphore_Close(&p->blocksSemaphore)); | ||
|  |     } | ||
|  |     RINOK_THREAD(Semaphore_Create(&p->blocksSemaphore, numBlocksMax, numBlocksMax)); | ||
|  |   } | ||
|  | 
 | ||
|  |   for (i = 0; i < MTCODER__BLOCKS_MAX - 1; i++) | ||
|  |     p->freeBlockList[i] = i + 1; | ||
|  |   p->freeBlockList[MTCODER__BLOCKS_MAX - 1] = (unsigned)(int)-1; | ||
|  |   p->freeBlockHead = 0; | ||
|  | 
 | ||
|  |   p->readProcessed = 0; | ||
|  |   p->blockIndex = 0; | ||
|  |   p->numBlocksMax = numBlocksMax; | ||
|  |   p->stopReading = False; | ||
|  | 
 | ||
|  |   #ifndef MTCODER__USE_WRITE_THREAD
 | ||
|  |     p->writeIndex = 0; | ||
|  |     p->writeRes = SZ_OK; | ||
|  |     for (i = 0; i < MTCODER__BLOCKS_MAX; i++) | ||
|  |       p->ReadyBlocks[i] = False; | ||
|  |     p->numFinishedThreads = 0; | ||
|  |   #endif
 | ||
|  | 
 | ||
|  |   p->numStartedThreadsLimit = numThreads; | ||
|  |   p->numStartedThreads = 0; | ||
|  | 
 | ||
|  |   // for (i = 0; i < numThreads; i++)
 | ||
|  |   { | ||
|  |     CMtCoderThread *nextThread = &p->threads[p->numStartedThreads++]; | ||
|  |     RINOK(MtCoderThread_CreateAndStart(nextThread)); | ||
|  |   } | ||
|  | 
 | ||
|  |   RINOK_THREAD(Event_Set(&p->readEvent)) | ||
|  | 
 | ||
|  |   #ifdef MTCODER__USE_WRITE_THREAD
 | ||
|  |   { | ||
|  |     unsigned bi = 0; | ||
|  | 
 | ||
|  |     for (;; bi++) | ||
|  |     { | ||
|  |       if (bi >= numBlocksMax) | ||
|  |         bi = 0; | ||
|  | 
 | ||
|  |       RINOK_THREAD(Event_Wait(&p->writeEvents[bi])) | ||
|  | 
 | ||
|  |       { | ||
|  |         const CMtCoderBlock *block = &p->blocks[bi]; | ||
|  |         unsigned bufIndex = block->bufIndex; | ||
|  |         Bool finished = block->finished; | ||
|  |         if (res == SZ_OK && block->res != SZ_OK) | ||
|  |           res = block->res; | ||
|  | 
 | ||
|  |         if (bufIndex != (unsigned)(int)-1) | ||
|  |         { | ||
|  |           if (res == SZ_OK) | ||
|  |           { | ||
|  |             res = p->mtCallback->Write(p->mtCallbackObject, bufIndex); | ||
|  |             if (res != SZ_OK) | ||
|  |               MtProgress_SetError(&p->mtProgress, res); | ||
|  |           } | ||
|  |            | ||
|  |           CriticalSection_Enter(&p->cs); | ||
|  |           { | ||
|  |             p->freeBlockList[bufIndex] = p->freeBlockHead; | ||
|  |             p->freeBlockHead = bufIndex; | ||
|  |           } | ||
|  |           CriticalSection_Leave(&p->cs); | ||
|  |         } | ||
|  |          | ||
|  |         RINOK_THREAD(Semaphore_Release1(&p->blocksSemaphore)) | ||
|  | 
 | ||
|  |         if (finished) | ||
|  |           break; | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  |   #else
 | ||
|  |   { | ||
|  |     WRes wres = Event_Wait(&p->finishedEvent); | ||
|  |     res = MY_SRes_HRESULT_FROM_WRes(wres); | ||
|  |   } | ||
|  |   #endif
 | ||
|  | 
 | ||
|  |   if (res == SZ_OK) | ||
|  |     res = p->readRes; | ||
|  | 
 | ||
|  |   if (res == SZ_OK) | ||
|  |     res = p->mtProgress.res; | ||
|  | 
 | ||
|  |   #ifndef MTCODER__USE_WRITE_THREAD
 | ||
|  |     if (res == SZ_OK) | ||
|  |       res = p->writeRes; | ||
|  |   #endif
 | ||
|  | 
 | ||
|  |   if (res != SZ_OK) | ||
|  |     MtCoder_Free(p); | ||
|  |   return res; | ||
|  | } |