Last active
August 29, 2015 13:57
-
-
Save derrickturk/9799767 to your computer and use it in GitHub Desktop.
Multi-threading, callbacks into VBA, DoEvents madness, and the mysterious vba_lock!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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