diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a6188e..9314923 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,7 +14,7 @@ message(STATUS "Compiler CXX ID: ${CMAKE_CXX_COMPILER_ID}")
 
 set(SRC_FILES
     src/of_path.cpp src/of_str.cpp
-    src/of_win.cpp
+    src/of_win.cpp src/of_util.cpp
 )
 
 include_directories(include)
diff --git a/include/of_util.h b/include/of_util.h
new file mode 100644
index 0000000..20a542a
--- /dev/null
+++ b/include/of_util.h
@@ -0,0 +1,31 @@
+#include "of_def.hpp"
+#include <cassert>
+#include <iostream>
+#include <memory>
+#include <mutex>
+
+namespace ofen {
+template <typename T> class OfSingleton
+{
+public:
+    OfSingleton(const OfSingleton&) = delete;
+    OfSingleton& operator=(const OfSingleton&) = delete;
+
+    static std::shared_ptr<T> getInstance()
+    {
+        std::call_once(init_flag, []() { instance.reset(new T()); });
+        return instance;
+    }
+
+protected:
+    OfSingleton() = default;
+    virtual ~OfSingleton() = default;
+
+private:
+    static std::shared_ptr<T> instance;
+    static std::once_flag init_flag;
+};
+
+template <typename T> std::shared_ptr<T> OfSingleton<T>::instance = nullptr;
+template <typename T> std::once_flag OfSingleton<T>::init_flag;
+}   // namespace ofen
diff --git a/src/of_util.cpp b/src/of_util.cpp
new file mode 100644
index 0000000..e282d86
--- /dev/null
+++ b/src/of_util.cpp
@@ -0,0 +1 @@
+#include "of_util.h"
\ No newline at end of file