summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2016-08-10 16:54:06 +0200
committerStanislaw Halik <sthalik@misaki.pl>2016-08-10 16:54:06 +0200
commit28ab0947c7fbf4224ece0d902be9f946807015d8 (patch)
tree5730670e1ae6f58334c05718cc414045d92e5696
parentcdacaf690a48a3cc1549d9c6e57de2100dc1fc21 (diff)
dinput: prevent freeing handle despite static initializer order
-rw-r--r--opentrack-dinput/dinput.cpp66
-rw-r--r--opentrack-dinput/dinput.hpp51
2 files changed, 83 insertions, 34 deletions
diff --git a/opentrack-dinput/dinput.cpp b/opentrack-dinput/dinput.cpp
index ce80fe0e..06b5e4f2 100644
--- a/opentrack-dinput/dinput.cpp
+++ b/opentrack-dinput/dinput.cpp
@@ -3,22 +3,11 @@
#include "dinput.hpp"
#include <QDebug>
-dinput_handle dinput_handle::self;
+std::atomic<int> dinput_handle::refcnt;
+std::atomic_flag dinput_handle::init_lock = ATOMIC_FLAG_INIT;
+dinput_handle::di_t dinput_handle::handle(dinput_handle::make_di());
-dinput_handle::dinput_handle() : handle(init_di())
-{
-}
-
-dinput_handle::~dinput_handle()
-{
- if (handle)
- {
- handle->Release();
- handle = nullptr;
- }
-}
-
-dinput_handle::di_t dinput_handle::init_di()
+LPDIRECTINPUT8& dinput_handle::init_di()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
@@ -27,13 +16,9 @@ dinput_handle::di_t dinput_handle::init_di()
static LPDIRECTINPUT8 di_ = nullptr;
if (di_ == nullptr)
{
- if (SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di_, NULL)))
- {
- return di_;
- }
- else
+ if (!SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&di_, NULL)))
{
- return di_ = nullptr;
+ di_ = nullptr;
}
}
return di_;
@@ -41,7 +26,44 @@ dinput_handle::di_t dinput_handle::init_di()
dinput_handle::di_t dinput_handle::make_di()
{
- return self.handle;
+ while (init_lock.test_and_set()) { /* busy loop */ }
+
+ LPDIRECTINPUT8& ret = init_di();
+
+ init_lock.clear();
+
+ return di_t(ret);
}
#endif
+
+dinput_handle::di_t::di_t(LPDIRECTINPUT8& handle) : handle(handle)
+{
+ while (init_lock.test_and_set()) { /* busy loop */ }
+
+ refcnt++;
+
+ init_lock.clear();
+}
+
+void dinput_handle::di_t::free_di()
+{
+ if (handle)
+ handle->Release();
+ handle = nullptr;
+}
+
+dinput_handle::di_t::~di_t()
+{
+ while (init_lock.test_and_set()) { /* busy loop */ }
+
+ int refcnt_ = refcnt.fetch_sub(1);
+
+ if (refcnt_ == 1)
+ {
+ qDebug() << "exit: deleting di handle";
+ free_di();
+ }
+
+ init_lock.clear();
+}
diff --git a/opentrack-dinput/dinput.hpp b/opentrack-dinput/dinput.hpp
index db901887..37a6d723 100644
--- a/opentrack-dinput/dinput.hpp
+++ b/opentrack-dinput/dinput.hpp
@@ -1,28 +1,55 @@
+/* Copyright (c) 2016, Stanislaw Halik <sthalik@misaki.pl>
+
+ * Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice and this permission
+ * notice appear in all copies.
+ */
+
#pragma once
#ifdef _WIN32
+#include "export.hpp"
+
#ifndef DIRECTINPUT_VERSION
# define DIRECTINPUT_VERSION 0x800
#endif
-#include "export.hpp"
#include <dinput.h>
-#include <windows.h>
-struct OPENTRACK_DINPUT_EXPORT dinput_handle final
+#include <atomic>
+
+class OPENTRACK_DINPUT_EXPORT dinput_handle final
{
- using di_t = LPDIRECTINPUT8;
+public:
+ class di_t;
+
private:
- static dinput_handle self;
- dinput_handle();
- ~dinput_handle();
- static di_t init_di();
- di_t handle;
+ static std::atomic<int> refcnt;
+ static std::atomic_flag init_lock;
+ static di_t handle;
+
+ static LPDIRECTINPUT8& init_di();
public:
- static di_t make_di();
+ class di_t final
+ {
+ friend class dinput_handle;
+
+ LPDIRECTINPUT8& handle;
+
+ di_t(LPDIRECTINPUT8& handle);
+ void free_di();
+
+ public:
+ LPDIRECTINPUT8 operator->() { return handle; }
+ operator LPDIRECTINPUT8() { return handle; }
+ LPDIRECTINPUT8 di() { return handle; }
+
+ ~di_t();
+ };
- dinput_handle(const dinput_handle&) = delete;
- dinput_handle(dinput_handle&&) = delete;
+ static di_t make_di();
+ dinput_handle() = delete;
};
#endif