Skip to content

Instantly share code, notes, and snippets.

@derrickturk
Last active August 29, 2015 13:57
Show Gist options
  • Save derrickturk/9799767 to your computer and use it in GitHub Desktop.
Save derrickturk/9799767 to your computer and use it in GitHub Desktop.
Multi-threading, callbacks into VBA, DoEvents madness, and the mysterious vba_lock!
CXX=g++
CXXOPTS=-std=c++11 -pedantic -Wall -Wextra -Werror -static -static-libgcc -static-libstdc++
OPTOPTS=-O3 -fno-rtti -ffast-math -mfpmath=387 -msse3 -mtune=core2 -funroll-loops -s
DLLOPTS=-shared -DDLL
LINKOPTS=-Wl,--add-stdcall-alias
LOG=
#LOG=-DLOGGING="log.txt"
thread_engine.dll: thread_engine.cpp
$(CXX) $(CXXOPTS) $(LOG) $(OPTOPTS) $(DLLOPTS) -o thread_engine.dll thread_engine.cpp $(LINKOPTS)
use_engine.exe: use_engine.cpp thread_engine.dll
$(CXX) $(CXXOPTS) $(LOG) $(OPTOPTS) -o use_engine.exe use_engine.cpp thread_engine.dll
clean:
-rm *.dll *.exe *.a *.o
#include "thread_engine.hpp"
#include <thread>
#include <mutex>
#include <atomic>
namespace {
std::mutex vba_mutex;
std::atomic<unsigned> rq_count(0);
void fill_buffer_impl(int begin[], int n, int tag, callback_fn callback)
noexcept;
}
void fill_buffer [[gnu::stdcall, USEDLL]] (int begin[], int n, int tag,
callback_fn callback)
{
rq_count.fetch_add(1, std::memory_order_relaxed);
std::thread t(fill_buffer_impl, begin, n, tag, callback);
t.detach();
}
bool any_pending [[gnu::stdcall, USEDLL]] () noexcept
{
return rq_count;
}
void wait_for_all [[gnu::stdcall, USEDLL]] () noexcept
{
while (rq_count);
}
namespace {
void fill_buffer_impl(int begin[], int n, int tag, callback_fn callback)
noexcept
{
for (int i = 0; i < n; ++i)
begin[i] = tag * (i + 1);
{
std::lock_guard<std::mutex> vba_lock(vba_mutex);
callback(tag);
}
rq_count.fetch_sub(1, std::memory_order_relaxed);
}
}
#ifndef THREAD_ENGINE_HPP
#ifdef DLL
#define USEDLL gnu::dllexport
#else
#define USEDLL gnu::dllimport
#endif
extern "C" {
using callback_fn [[gnu::stdcall]] = void (*)(int);
void fill_buffer [[gnu::stdcall, USEDLL]] (int begin[], int n, int tag,
callback_fn callback);
bool any_pending [[gnu::stdcall, USEDLL]] () noexcept;
void wait_for_all [[gnu::stdcall, USEDLL]] () noexcept;
}
#define THREAD_ENGINE_HPP
#endif
Option Explicit
Public Declare Sub fill_buffer Lib "thread_engine.dll" (ByRef buffer As Long, _
ByVal n As Long, ByVal buffer_tag As Long, ByVal callback_ptr As Long)
Public Declare Function any_pending Lib "thread_engine.dll" () As Byte
Public Declare Sub wait_for_all Lib "thread_engine.dll" () ' bad dog!
Private buffers(1 To 10, 1 To 10) As Long
Public Sub launch_threads
Dim i As Long
ChDir ActiveWorkbook.Path
For i = 1 To 10
' lookee here: they're column-major
fill_buffer buffers(1, i), 10, i, AddressOf notify_thread
Next i
While any_pending()
DoEvents
Wend
End Sub
Public Sub notify_thread(ByVal thread_tag As Long)
Debug.Print "Finished filling buffer " & thread_tag
Debug.Print "Its last entry is " & buffers(10, thread_tag)
End Sub
#include "thread_engine.hpp"
#include <iostream>
#include <mutex>
#include <atomic>
void notify [[gnu::stdcall]] (int tag) noexcept;
int main()
{
std::array<std::array<int, 10>, 10> results;
for (int i = 0; i < 10; ++i)
fill_buffer(results[i].data(), 10, i, notify);
wait_for_all();
}
void notify(int tag) noexcept
{
std::cout << "finished calculation for tag " << tag << '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment