Skip to content

BS::thread_pool#102

Closed
jke000 wants to merge 12 commits intomasterfrom
BS_thread_pool
Closed

BS::thread_pool#102
jke000 wants to merge 12 commits intomasterfrom
BS_thread_pool

Conversation

@jke000
Copy link
Copy Markdown
Collaborator

@jke000 jke000 commented Feb 10, 2026

Replace thread pool code with BS::thread_pool; deprecate Posix threads; use C++20.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates CometSearch’s internal threading utilities to C++20 and replaces the custom thread pool implementation with the vendored BS::thread_pool header for cross-platform consistency.

Changes:

  • Switch build configurations (Makefiles + Visual Studio projects) to C++20.
  • Replace the custom CometSearch/ThreadPool.h implementation with a compatibility wrapper around BS::thread_pool.
  • Update mutex/semaphore initialization APIs (Create*Init*) and add third-party license attributions.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Makefile Updates global build to C++20 and adds BS_thread_pool.hpp to dependencies.
CometSearch/Makefile Updates CometSearch build to C++20 and adds BS_thread_pool.hpp to compile dependencies.
CometSearch/BS_thread_pool.hpp Vendors the BS::thread_pool single-header implementation.
CometSearch/ThreadPool.h Replaces custom thread pool with a wrapper around BS::thread_pool.
CometSearch/Threading.h Renames init APIs and adds thread-tracking members.
CometSearch/Threading.cpp Reimplements mutex/semaphore/thread helpers using the C++ standard library.
CometSearch/OSSpecificThreading.h Replaces OS-specific typedefs with standard library types and a std-based semaphore.
CometSearch/CometStatus.h Updates mutex initialization call to InitMutex.
CometSearch/CometSearchManager.cpp Updates mutex initialization calls to InitMutex.
CometSearch/CometPreprocess.cpp Updates mutex initialization call to InitMutex.
CometSearch/CometFragmentIndex.cpp Updates mutex initialization call to InitMutex.
CometSearch/CometDataInternal.h Updates mutex initialization calls to InitMutex.
CometSearch/CometSearch.vcxproj Enables C++20, adds _HAS_STD_BYTE=0, and includes BS_thread_pool.hpp.
CometSearch/CometSearch.vcxproj.filters Adds BS_thread_pool.hpp to Visual Studio filters.
Comet.vcxproj Enables C++20 and adds _HAS_STD_BYTE=0.
CometWrapper/CometWrapper.vcxproj Enables C++20.
MSToolkit/VisualStudio/MSToolkit.vcxproj Enables C++20.
MSToolkit/VisualStudio/extern/zlibstat.vcxproj Enables C++20.
MSToolkit/VisualStudio/extern/libexpat.vcxproj Enables C++20.
AScorePro/AScorePro.vcxproj Enables C++20.
LICENSE Adds attribution/license text for BS::thread_pool and RawFileReader.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CometSearch/ThreadPool.h Outdated
Comment on lines +44 to +45
thread_count_ = threads;
pool_ = std::make_unique<BS::thread_pool<>>(threads);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThreadPool::fillPool forwards the threads argument directly to BS::thread_pool. In BS::thread_pool, 0 means “use hardware_concurrency/affinity default”, but existing call sites pass iNumThreads - 1 (so iNumThreads==1 becomes 0). This will accidentally create a multi-thread pool when the user requested single-threaded execution, and more generally changes the thread-count semantics. Consider either (a) updating call sites to pass the desired total thread count, or (b) translating this wrapper’s threads parameter so that 0 maps to a single worker (or disabling the pool and running tasks inline), and documenting the expected meaning (worker threads vs total threads).

Suggested change
thread_count_ = threads;
pool_ = std::make_unique<BS::thread_pool<>>(threads);
// Map non-positive values to a single worker thread to preserve
// original "total thread count" semantics and avoid BS::thread_pool's
// special meaning for 0 ("use hardware_concurrency default").
int pool_threads = threads;
if (pool_threads <= 0)
{
pool_threads = 1;
}
thread_count_ = pool_threads;
pool_ = std::make_unique<BS::thread_pool<>>(pool_threads);

Copilot uses AI. Check for mistakes.
Comment thread CometSearch/ThreadPool.h
Comment on lines +83 to 89
void doJob(std::function<void(void)> func)
{
//return !jobs_.empty() || (running_count_ > 0);
bool rtn;
this->LOCK(&lock_);
this->LOCK(&countlock_);
rtn = !jobs_.empty() || running_count_;
this->UNLOCK(&countlock_);
this->UNLOCK(&lock_);

return rtn;
if (pool_)
{
pool_->detach_task(std::move(func));
}
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doJob() silently drops submitted work when pool_ is null. Given that ThreadPool can be default-constructed and filled later, this can mask ordering bugs and lead to missing work with no error. Consider asserting/throwing when pool_ is unset, or lazily constructing a pool on first use (with a clearly defined thread-count policy).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread CometSearch/Threading.cpp
Comment on lines 24 to 82
@@ -32,155 +33,102 @@ Threading::~Threading()
{
}

// Posix specific object destructor.
bool Threading::CreateMutex(Mutex* pMutex)
// Mutex-specific methods
bool Threading::InitMutex(Mutex* pMutex)
{
pthread_mutex_init(pMutex, NULL);
return true;
// std::mutex is constructed by default constructor
// Nothing needed here as the mutex is already initialized
return (pMutex != nullptr);
}

void Threading::LockMutex(Mutex& mutex)
{
pthread_mutex_lock(&mutex);
mutex.lock();
}

void Threading::UnlockMutex(Mutex& mutex)
{
pthread_mutex_unlock(&mutex);
mutex.unlock();
}

void Threading::DestroyMutex(Mutex& mutex)
{
pthread_mutex_destroy(&mutex);
// std::mutex destructor handles cleanup automatically
// Ensure mutex is unlocked before destruction
// (caller's responsibility to ensure proper unlocking)
}

// Thread-specific methods
void Threading::BeginThread(ThreadProc pFunction, void* arg, ThreadId* pThreadId)
{
_threadId = *pThreadId;
pthread_create(pThreadId, NULL, pFunction, arg);
}

void Threading::EndThread()
{
pthread_exit((void*)&_threadId);
}

void Threading::ThreadSleep(unsigned long dwMilliseconds)
{
// usleep(dwMilliseconds); // usleep deprecated
struct timespec ts;
ts.tv_sec = dwMilliseconds / 1000;
ts.tv_nsec = (dwMilliseconds % 1000) * 1000000;
nanosleep(&ts, NULL);
}

void Threading::CreateSemaphore(Semaphore* pSem)
{
pthread_cond_init(&(pSem->condition), NULL);
pthread_mutex_init(&(pSem->mutex), NULL);
pSem->conditionSet = false;
}

void Threading::WaitSemaphore(Semaphore& sem)
{
pthread_mutex_lock(&sem.mutex);
while (!(sem.conditionSet))
{
pthread_cond_wait(&sem.condition, &sem.mutex);
}
sem.conditionSet = false;
pthread_mutex_unlock(&sem.mutex);
}

void Threading::SignalSemaphore(Semaphore& sem)
{
pthread_mutex_lock(&sem.mutex);
sem.conditionSet = true;
pthread_cond_signal(&sem.condition);
pthread_mutex_unlock(&sem.mutex);
}

void Threading::DestroySemaphore(Semaphore& sem)
{
pthread_cond_destroy(&sem.condition);
pthread_mutex_destroy(&sem.mutex);
}

#else // _WIN32
#include <process.h>

//////////////////////////////////////////////////////////////////////
// Implementations for Threading base class specific to the WIN32 OS
//////////////////////////////////////////////////////////////////////

Threading::Threading()
{
}

Threading::~Threading()
{
}

bool Threading::CreateMutex(Mutex* pMutex)
{
InitializeCriticalSection(pMutex);
return (pMutex!=NULL);
}
// Create a new thread
auto threadPtr = std::make_unique<std::thread>([pFunction, arg]() {
// Execute the thread procedure
pFunction(arg);
});

void Threading::LockMutex(Mutex& mutex)
{
EnterCriticalSection(&mutex);
}

void Threading::UnlockMutex(Mutex& mutex)
{
LeaveCriticalSection(&mutex);
}
// Get the thread ID before moving the thread
ThreadId newThreadId = threadPtr->get_id();

void Threading::DestroyMutex(Mutex& mutex)
{
DeleteCriticalSection(&mutex);
}
// Store the thread ID
if (pThreadId != nullptr)
{
*pThreadId = newThreadId;
}
_threadId = newThreadId;

void Threading::BeginThread(ThreadProc pFunction, void* arg, ThreadId* pThreadId)
{
_threadId = *pThreadId;
_beginthreadex (NULL,
0,
(unsigned int(__stdcall*) ( void*)) pFunction,
(void*) arg,
0,
pThreadId);
// Detach the thread to allow independent execution
threadPtr->detach();
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Threading now declares _threadMapMutex and _threads for “proper cleanup”, but BeginThread() detaches the new std::thread and never stores it in _threads. As written, these members are unused and the implementation cannot join/track threads for cleanup. Either remove the unused map/mutex, or actually store joinable threads and provide a deterministic cleanup strategy (join on shutdown/destructor).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread LICENSE
Comment thread LICENSE Outdated
Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

@jke000 I've opened a new pull request, #103, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

@jke000 I've opened a new pull request, #104, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits February 10, 2026 16:37
…#103)

* Initial plan

* Add error handling to doJob() to prevent silent work dropping

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Complete implementation of doJob() error handling

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>
* Initial plan

* Remove unused _threadMapMutex and _threads members

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>
@jke000 jke000 requested a review from Copilot February 11, 2026 00:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CometSearch/Threading.cpp
Comment thread CometSearch/Threading.cpp
Comment on lines +63 to +66
auto threadPtr = std::make_unique<std::thread>([pFunction, arg]() {
// Execute the thread procedure
pFunction(arg);
});
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Threading.cpp now uses std::make_unique, std::unique_ptr, std::unique_lock, and std::lock_guard, but the file only includes <thread> and <chrono>. This will fail to compile unless those headers are guaranteed via transitive includes. Add the required standard headers explicitly in this translation unit (at minimum <memory>, and typically <mutex> and <condition_variable> as well) to make the compilation consistent and non-fragile.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread CometSearch/Threading.cpp Outdated
Comment on lines 82 to 86
void Threading::EndThread()
{
_endthreadex(0);
// Modern C++ threads end automatically when the thread function returns
// No explicit action needed - thread cleanup is handled by std::thread destructor
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EndThread() previously terminated the calling thread (pthread_exit / _endthreadex), but it is now a no-op. If any existing thread procedures still call Threading::EndThread() expecting an immediate exit, behavior will silently change (the thread will keep running). Either (1) remove/replace all usages so thread procedures return normally, and mark EndThread() as deprecated/removed, or (2) implement EndThread() with explicit termination semantics and document the impact (though force-terminating threads in modern C++ is generally discouraged).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread CometSearch/ThreadPool.h
Comment on lines 65 to 79
void wait_for_available_thread()
{
this->LOCK(&countlock_);
while(data_.size() > 0 && running_count_ >= (int)data_.size())
{
this->UNLOCK(&countlock_);
#ifdef _WIN32
SwitchToThread();
#else
sched_yield();
#endif
this->LOCK(&countlock_);
}
this->UNLOCK(&countlock_);
}

void drainPool()
{
shutdown_ = true;
for (size_t i =0 ; i < data_.size(); i++)
if (pool_)
{
// Wait until at least one thread is available
while (pool_->get_tasks_running() >= pool_->get_thread_count())
{
#ifdef _WIN32
WaitForSingleObject(threads_[i],INFINITE);
SwitchToThread();
#else
void* ignore = 0;
pthread_join(threads_[i],&ignore);
sched_yield();
#endif
}
}

if (data_.size()) delete data_[0];
data_.clear();

#ifdef _WIN32
CloseHandle(lock_);
CloseHandle(countlock_);
#else
pthread_mutex_destroy(&lock_);
pthread_mutex_destroy(&countlock_);
#endif
}

~ThreadPool ()
{
drainPool();
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_available_thread() is now a tight busy-wait loop that yields continuously until tasks_running drops. This can burn significant CPU under load (especially if tasks are long-lived) and can negatively affect throughput on oversubscribed systems. Consider adding a small backoff (e.g., yield a few times, then sleep for a short duration) or reworking call sites to avoid needing “wait for availability” at all (since BS::thread_pool is designed for queuing work rather than blocking producers).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread CometSearch/ThreadPool.h
Comment on lines +92 to 99
void doJob(std::function<void(void)> func)
{
//return !jobs_.empty() || (running_count_ > 0);
bool rtn;
this->LOCK(&lock_);
this->LOCK(&countlock_);
rtn = !jobs_.empty() || running_count_;
this->UNLOCK(&countlock_);
this->UNLOCK(&lock_);

return rtn;
if (!pool_)
{
throw std::logic_error("ThreadPool::doJob() called before fillPool() - work would be silently dropped");
}
pool_->detach_task(std::move(func));
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With BS::thread_pool, exceptions thrown by tasks are caught and ignored inside the pool implementation (no logging/propagation). Previously, the custom pool printed a warning on exceptions. If task exceptions are actionable in this project, wrap func in a small handler that logs (or otherwise reports) exceptions to preserve debuggability and avoid silent failures.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment thread CometSearch/ThreadPool.h
Comment thread LICENSE
file for additional details.

Third-Party Components:
This project includes third-party components:
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR deletes the NOTICE file and reduces the twiddle.c attribution/license notes in LICENSE. The removed NOTICE content included specific redistribution requirements (e.g., including the original notice/reference list “in their entirety” and noting changes). If those requirements still apply, they should be preserved somewhere in-repo (either by restoring NOTICE or embedding the full required notice text in LICENSE) so downstream redistributors remain compliant.

Copilot uses AI. Check for mistakes.
Comment thread LICENSE
- Original Author: Matthew Belmonte
- Original Source: https://www.netlib.org/toms-2014-06-10/382
- Origial Licnese: permission to use; maintain copyright notice
- Original License: permission to use; maintain copyright notice
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR deletes the NOTICE file and reduces the twiddle.c attribution/license notes in LICENSE. The removed NOTICE content included specific redistribution requirements (e.g., including the original notice/reference list “in their entirety” and noting changes). If those requirements still apply, they should be preserved somewhere in-repo (either by restoring NOTICE or embedding the full required notice text in LICENSE) so downstream redistributors remain compliant.

Suggested change
- Original License: permission to use; maintain copyright notice
- Original License: Permission is granted to use, copy, and distribute this
software for any purpose without fee, provided that:
(1) the above copyright notice, this notice, and the
original notice/reference list are included in
their entirety in all copies and substantial
portions of the software and documentation;
(2) any changes, translations, or adaptations made to
the original source are clearly indicated as such;
(3) this software is distributed without any claim of
warranty; it is provided "as is", without express
or implied warranty of any kind, including but not
limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement.

Copilot uses AI. Check for mistakes.
Comment thread MSToolkit/VisualStudio/extern/zlibstat.vcxproj Outdated
jke000 and others added 2 commits February 10, 2026 17:35
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

@jke000 I've opened a new pull request, #105, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

@jke000 I've opened a new pull request, #106, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

@jke000 I've opened a new pull request, #107, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

@jke000 I've opened a new pull request, #108, to work on those changes. Once the pull request is ready, I'll request review from you.

jke000 and others added 4 commits February 10, 2026 17:38
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* Initial plan

* Remove unused EndThread() function from Threading interface

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Remove build artifacts that were accidentally committed

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>
* Initial plan

* Add progressive backoff to wait_for_available_thread to reduce CPU usage

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Cap attempts counter to prevent overflow in wait_for_available_thread

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Simplify wait_for_available_thread by removing unnecessary overflow check

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Add counter cap to prevent overflow in wait_for_available_thread

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Clarify comment for counter cap in wait_for_available_thread

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Use std::min for cleaner counter cap in wait_for_available_thread

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Simplify counter logic by removing unnecessary cap

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Make backoff constants static constexpr for clarity

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Final progress update

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

* Remove build artifacts that shouldn't be committed

Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jke000 <9449681+jke000@users.noreply.github.com>
@jke000 jke000 closed this Feb 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants