improvements in thread safety and repetition detection in textthread

This commit is contained in:
Akash Mozumdar 2019-01-01 15:15:09 -05:00
parent 8880d27dc5
commit f080656e60
4 changed files with 28 additions and 25 deletions

View File

@ -120,7 +120,7 @@ namespace
SetWindowsHookExW(WH_GETMESSAGE, [](int statusCode, WPARAM wParam, LPARAM lParam) SetWindowsHookExW(WH_GETMESSAGE, [](int statusCode, WPARAM wParam, LPARAM lParam)
{ {
if (statusCode == HC_ACTION && wParam == PM_REMOVE && ((MSG*)lParam)->message == WM_CLIPBOARDUPDATE) if (statusCode == HC_ACTION && wParam == PM_REMOVE && ((MSG*)lParam)->message == WM_CLIPBOARDUPDATE)
if (auto text = Util::GetClipboardText()) Host::GetThread(CLIPBOARD)->PushSentence(text.value()); if (auto text = Util::GetClipboardText()) Host::GetThread(CLIPBOARD)->AddSentence(text.value());
return CallNextHookEx(NULL, statusCode, wParam, lParam); return CallNextHookEx(NULL, statusCode, wParam, lParam);
}, NULL, GetCurrentThreadId()); }, NULL, GetCurrentThreadId());
} }
@ -206,6 +206,6 @@ namespace Host
void AddConsoleOutput(std::wstring text) void AddConsoleOutput(std::wstring text)
{ {
GetThread(CONSOLE)->PushSentence(text); GetThread(CONSOLE)->AddSentence(text);
} }
} }

View File

@ -10,7 +10,7 @@ TextThread::TextThread(ThreadParam tp, HookParam hp, std::optional<std::wstring>
tp(tp), tp(tp),
hp(hp) hp(hp)
{ {
CreateTimerQueueTimer(timer, NULL, [](void* This, BOOLEAN) { ((TextThread*)This)->Flush(); }, this, 25, 25, WT_EXECUTELONGFUNCTION); CreateTimerQueueTimer(&timer, NULL, [](void* This, BOOLEAN) { ((TextThread*)This)->Flush(); }, this, 10, 10, WT_EXECUTELONGFUNCTION);
OnCreate(this); OnCreate(this);
} }
@ -19,6 +19,11 @@ TextThread::~TextThread()
OnDestroy(this); OnDestroy(this);
} }
void TextThread::AddSentence(const std::wstring& sentence)
{
queuedSentences->push_back(sentence);
}
void TextThread::Push(const BYTE* data, int len) void TextThread::Push(const BYTE* data, int len)
{ {
if (len < 0) return; if (len < 0) return;
@ -26,29 +31,27 @@ void TextThread::Push(const BYTE* data, int len)
if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, len / 2); if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, len / 2);
else if (auto converted = Util::StringToWideString(std::string((char*)data, len), hp.codepage ? hp.codepage : defaultCodepage)) buffer += converted.value(); else if (auto converted = Util::StringToWideString(std::string((char*)data, len), hp.codepage ? hp.codepage : defaultCodepage)) buffer += converted.value();
else Host::AddConsoleOutput(INVALID_CODEPAGE); else Host::AddConsoleOutput(INVALID_CODEPAGE);
if (std::all_of(buffer.begin(), buffer.end(), [&](wchar_t c) { return repeatingChars.count(c) > 0; })) buffer.clear();
lastPushTime = GetTickCount(); lastPushTime = GetTickCount();
}
void TextThread::PushSentence(std::wstring sentence) if (std::all_of(buffer.begin(), buffer.end(), [&](wchar_t c) { return repeatingChars.count(c) > 0; })) buffer.clear();
if (Util::RemoveRepetition(buffer)) // repetition detected, which means the entire sentence has already been received
{ {
LOCK(bufferMutex); repeatingChars = std::unordered_set(buffer.begin(), buffer.end());
buffer += sentence; AddSentence(buffer);
lastPushTime = 0; buffer.clear();
}
} }
void TextThread::Flush() void TextThread::Flush()
{ {
std::wstring sentence; std::vector<std::wstring> sentences;
{ queuedSentences->swap(sentences);
for (auto sentence : sentences)
if (Output(this, sentence)) storage->append(sentence);
LOCK(bufferMutex); LOCK(bufferMutex);
if (buffer.empty()) return; if (buffer.empty()) return;
if (buffer.size() < maxBufferSize && GetTickCount() - lastPushTime < flushDelay) return; if (buffer.size() < maxBufferSize && GetTickCount() - lastPushTime < flushDelay) return;
sentence = buffer; AddSentence(buffer);
buffer.clear(); buffer.clear();
if (Util::RemoveRepetition(sentence)) repeatingChars = std::unordered_set(sentence.begin(), sentence.end());
else repeatingChars.clear();
}
if (Output(this, sentence)) storage->append(sentence);
} }

View File

@ -19,9 +19,8 @@ public:
TextThread(ThreadParam tp, HookParam hp, std::optional<std::wstring> name = {}); TextThread(ThreadParam tp, HookParam hp, std::optional<std::wstring> name = {});
~TextThread(); ~TextThread();
void AddSentence(const std::wstring& sentence);
void Push(const BYTE* data, int len); void Push(const BYTE* data, int len);
// Flushes ASAP
void PushSentence(std::wstring sentence);
ThreadSafe<std::wstring> storage; ThreadSafe<std::wstring> storage;
const int64_t handle; const int64_t handle;
@ -37,5 +36,6 @@ private:
std::unordered_set<wchar_t> repeatingChars; std::unordered_set<wchar_t> repeatingChars;
std::mutex bufferMutex; std::mutex bufferMutex;
DWORD lastPushTime; DWORD lastPushTime;
ThreadSafe<std::vector<std::wstring>> queuedSentences;
AutoHandle<TimerDeleter> timer = NULL; // this needs to be last so it's destructed first AutoHandle<TimerDeleter> timer = NULL; // this needs to be last so it's destructed first
}; };

View File

@ -9,7 +9,7 @@ template<typename E, typename M = std::mutex>
class ThreadSafe class ThreadSafe
{ {
public: public:
template <typename ...Args> ThreadSafe(Args ...args) : contents(args...) {} template <typename... Args> ThreadSafe(Args&&... args) : contents(std::forward<Args>(args)...) {}
auto operator->() auto operator->()
{ {
struct struct
@ -33,7 +33,7 @@ class AutoHandle
public: public:
AutoHandle(HANDLE h) : h(h) {} AutoHandle(HANDLE h) : h(h) {}
operator HANDLE() { return h.get(); } operator HANDLE() { return h.get(); }
operator PHANDLE() { return &h._Myptr(); } PHANDLE operator&() { return &h._Myptr(); }
operator bool() { return h.get() != NULL && h.get() != INVALID_HANDLE_VALUE; } operator bool() { return h.get() != NULL && h.get() != INVALID_HANDLE_VALUE; }
private: private: