657 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* 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;
 | 
						|
}
 |