ithewei hace 6 años
padre
commit
99471e17ab
Se han modificado 12 ficheros con 251 adiciones y 78 borrados
  1. 14 9
      Makefile
  2. 54 34
      README.md
  3. 24 15
      base/hscope.h
  4. 28 19
      base/hthreadpool.h
  5. 2 0
      h.h
  6. 29 0
      unittest/connect_test.c
  7. 19 0
      unittest/defer_test.cpp
  8. 16 0
      unittest/ifconfig_test.cpp
  9. 34 0
      unittest/listdir_test.cpp
  10. 0 0
      unittest/ping_test.c
  11. 29 0
      unittest/threadpool_test.cpp
  12. 2 1
      unittest/webbench.c

+ 14 - 9
Makefile

@@ -3,7 +3,7 @@ TMPDIR=tmp
 
 default: all
 
-all: libhw test ping timer loop tcp udp nc nmap httpd
+all: libhw test timer loop tcp udp nc nmap httpd curl
 
 clean:
 	$(MAKEF) clean SRCDIRS=". base utils event http http/client http/server examples $(TMPDIR)"
@@ -20,10 +20,6 @@ test: prepare
 	cp main.cpp.tmpl $(TMPDIR)/main.cpp
 	$(MAKEF) TARGET=$@ SRCDIRS=". base utils $(TMPDIR)"
 
-ping:
-	-rm base/hsocket.o
-	$(MAKEF) TARGET=$@ SRCDIRS="" SRCS="examples/ping.c base/hsocket.c base/htime.c base/RAII.cpp" INCDIRS=". base" DEFINES="$(DEFINES) PRINT_DEBUG"
-
 timer: prepare
 	-rm $(TMPDIR)/*.o $(TMPDIR)/*.h $(TMPDIR)/*.c $(TMPDIR)/*.cpp
 	cp examples/timer.c $(TMPDIR)
@@ -64,13 +60,22 @@ httpd: prepare
 	cp examples/httpd.cpp examples/http_api_test.h $(TMPDIR)
 	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event http http/server $(TMPDIR)"
 
-webbench:
-	$(MAKEF) TARGET=$@ SRCDIRS="" SRCS="examples/webbench.c"
-
 curl:
 	-rm $(TMPDIR)/*.o $(TMPDIR)/*.h $(TMPDIR)/*.c $(TMPDIR)/*.cpp
 	cp examples/curl.cpp $(TMPDIR)
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CURL_SRCDIRS)" SRCDIRS=". base utils http http/client $(TMPDIR)"
 	#$(MAKEF) TARGET=$@ SRCDIRS="$(CURL_SRCDIRS)" SRCDIRS=". base utils http http/client $(TMPDIR)" DEFINES="$(DEFINES) WITH_CURL CURL_STATICLIB"
 
-.PHONY: clean prepare libhw test ping timer loop tcp udp nc nmap httpd webbench curl
+unittest:
+	$(CC)  -std=c99   -I. -Ibase         -o bin/ping       unittest/ping_test.c          base/hsocket.c base/htime.c -DPRINT_DEBUG
+	$(CC)  -std=c99   -I. -Ibase         -o bin/connect    unittest/connect_test.c       base/hsocket.c base/htime.c
+	$(CXX) -std=c++11 -I. -Ibase         -o bin/defer      unittest/defer_test.cpp
+	$(CXX) -std=c++11 -I. -Ibase         -o bin/threadpool unittest/threadpool_test.cpp  -pthread
+	$(CXX) -std=c++11 -I. -Ibase         -o bin/ls         unittest/listdir_test.cpp     base/hdir.cpp base/hbase.c
+	$(CXX) -std=c++11 -I. -Ibase -Iutils -o bin/ifconfig   unittest/ifconfig_test.cpp    utils/ifconfig.cpp
+
+# UNIX only
+webbench:
+	$(CC) -o bin/webbench unittest/webbench.c
+
+.PHONY: clean prepare libhw test timer loop tcp udp nc nmap httpd curl unittest webbench

+ 54 - 34
README.md

@@ -14,35 +14,45 @@ hw 是一套跨平台c/c++基础组件,函数名/类名以h/H开头
 
 ## Module
 
+### data-structure
+- array.h:       动态数组
+- list.h:        链表
+- queue.h:       队列
+- heap.h:        堆
+
 ### base
-- hplatform.h: 平台相关
-- hdef.h: 宏定义
-- hversion.h: 版本
-- hsysinfo.h: 系统信息
-- hproc.h: 子进程/线程类
-- htime.h: 时间
-- herr.h: 错误码
-- hlog.h: 日志
-- hstring.h: 字符串
-- hvar.h: var变量
-- hobj.h: 对象基类
-- hgui.h: gui相关定义
-- hbuf.h: 缓存类
-- hfile.h: 文件类
-- hscope.h: 作用域RAII机制
-- hmutex.h:同步锁
-- hthread.h:线程
+- hplatform.h:   平台相关宏
+- hdef.h:        宏定义
+- hversion.h:    版本
+- hbase.h:       基本接口
+- hsysinfo.h:    系统信息
+- hproc.h:       子进程/线程类
+- hmath.h:       math扩展函数
+- htime.h:       时间
+- herr.h:        错误码
+- hlog.h:        日志
+- hsocket.h:     socket操作
+- hstring.h:     字符串
+- hvar.h:        var变量
+- hobj.h:        对象基类
+- hgui.h:        gui相关定义
+- hbuf.h:        缓存类
+- hfile.h:       文件类
+- hdir.h:        ls实现
+- hscope.h:      作用域RAII机制
+- hmutex.h:     同步锁
+- hthread.h:    线程
 - hthreadpool.h:线程池
 
 ### utils
-- hendian.h: 大小端
-- hmain.h: main_ctx: arg env
-- ifconfig.h: ifconfig实现
-- singleton.h: 单例模式
-- iniparser.h: ini解析
+- hmain.h:       main_ctx: arg env
+- hendian.h:     大小端
+- ifconfig.h:    ifconfig实现
+- iniparser.h:   ini解析
+- singleton.h:   单例模式
 
 ### event
-- hloop.h: 事件循环
+- hloop.h:       事件循环
 
 ### http
 - http_client.h: http客户端
@@ -50,8 +60,8 @@ hw 是一套跨平台c/c++基础组件,函数名/类名以h/H开头
 
 ### other
 
-- h.h:总头文件
-- Makefile.in: 通用Makefile模板
+- h.h:          总头文件
+- Makefile.in:   通用Makefile模板
 - main.cpp.tmpl: 通用main.cpp模板
 
 ## BUILD
@@ -59,16 +69,26 @@ hw 是一套跨平台c/c++基础组件,函数名/类名以h/H开头
 ### examples
 
 - make all
-- make test: 服务端master-workers model
+- make test:  服务端master-workers model
 - make timer: 定时器测试
-- make loop: 事件循环(包含timer、io、idle)
-- make tcp:  tcp server
-- make udp:  udp server
-- make nc:   network client
-- make nmap: host discovery
+- make loop:  事件循环(包含timer、io、idle)
+- make tcp:   tcp server
+- make udp:   udp server
+- make nc:    network client
+- make nmap:  host discovery
 - make httpd: http服务(包含web service和api service)
-- make curl: 基于libcurl封装http客户端
+- make curl:  基于libcurl封装http客户端
+
+### tests
 - make webbench: http服务压力测试程序
+- make unittest: 单元测试
+
+### compile options
+#### compile with print debug info
+- make DEFINES=PRINT_DEBUG
+
+#### compile WITH_OPENSSL
+- make DEFINES=WITH_OPENSSL
 
-### compile with print debug info
-- make all DEFINES=PRINT_DEBUG
+#### compile WITH_CURL
+- make DEFINES="WITH_CURL CURL_STATICLIB"

+ 24 - 15
base/hscope.h

@@ -2,13 +2,22 @@
 #define HW_SCOPE_H_
 
 #include <functional>
+typedef std::function<void()> Function;
 
 #include "hdef.h"
 
-class ScopeCleanup {
- public:
-    typedef std::function<void()> FT;
+// same as golang defer
+class Defer {
+public:
+    Defer(Function&& fn) : _fn(std::move(fn)) {}
+    ~Defer() { if(_fn) _fn();}
+private:
+    Function _fn;
+};
+#define defer(code) Defer STRINGCAT(_defer_, __LINE__)([&](){code});
 
+class ScopeCleanup {
+public:
     template<typename Fn, typename... Args>
     ScopeCleanup(Fn&& fn, Args&&... args) {
         cleanup_ = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);
@@ -18,52 +27,52 @@ class ScopeCleanup {
         cleanup_();
     }
 
- private:
-    FT cleanup_;
+private:
+    Function cleanup_;
 };
 
 template<typename T>
 class ScopeFree {
- public:
+public:
     ScopeFree(T* p) : _p(p) {}
     ~ScopeFree()    {SAFE_FREE(_p);}
- private:
+private:
     T*  _p;
 };
 
 template<typename T>
 class ScopeDelete {
- public:
+public:
     ScopeDelete(T* p) : _p(p) {}
     ~ScopeDelete()    {SAFE_DELETE(_p);}
- private:
+private:
     T*  _p;
 };
 
 template<typename T>
 class ScopeDeleteArray {
- public:
+public:
     ScopeDeleteArray(T* p) : _p(p) {}
     ~ScopeDeleteArray()    {SAFE_DELETE_ARRAY(_p);}
- private:
+private:
     T*  _p;
 };
 
 template<typename T>
 class ScopeRelease {
- public:
+public:
     ScopeRelease(T* p) : _p(p) {}
     ~ScopeRelease()    {SAFE_RELEASE(_p);}
- private:
+private:
     T*  _p;
 };
 
 template<typename T>
 class ScopeLock {
- public:
+public:
     ScopeLock(T& mutex) : _mutex(mutex) {_mutex.lock();}
     ~ScopeLock()    {_mutex.unlock();}
- private:
+private:
     T& _mutex;
 };
 

+ 28 - 19
base/hthreadpool.h

@@ -12,11 +12,11 @@
 #include <memory>
 #include <utility>
 
-#include "hlog.h"
+//#include "hlog.h"
 #include "hthread.h"
 
 class HThreadPool {
- public:
+public:
     using Task = std::function<void()>;
 
     HThreadPool(int size = std::thread::hardware_concurrency())
@@ -32,7 +32,7 @@ class HThreadPool {
             status = RUNNING;
             for (int i = 0; i < pool_size; ++i) {
                 workers.emplace_back(std::thread([this]{
-                    hlogd("work thread[%X] running...", gettid());
+                    //hlogd("work thread[%X] running...", gettid());
                     while (status != STOP) {
                         while (status == PAUSE) {
                             std::this_thread::yield();
@@ -40,20 +40,20 @@ class HThreadPool {
 
                         Task task;
                         {
-                            std::unique_lock<std::mutex> locker(mutex);
-                            cond.wait(locker, [this]{
+                            std::unique_lock<std::mutex> locker(_mutex);
+                            _cond.wait(locker, [this]{
                                 return status == STOP || !tasks.empty();
                             });
 
                             if (status == STOP) return;
 
                             if (!tasks.empty()) {
+                                --idle_num;
                                 task = std::move(tasks.front());
                                 tasks.pop();
                             }
                         }
 
-                        --idle_num;
                         task();
                         ++idle_num;
                     }
@@ -66,9 +66,9 @@ class HThreadPool {
     int stop() {
         if (status != STOP) {
             status = STOP;
-            cond.notify_all();
-            for (auto& thread : workers) {
-                thread.join();
+            _cond.notify_all();
+            for (auto& worker : workers) {
+                worker.join();
             }
         }
         return 0;
@@ -88,6 +88,15 @@ class HThreadPool {
         return 0;
     }
 
+    int wait() {
+        while (1) {
+            if (status == STOP || (tasks.empty() && idle_num == pool_size)) {
+                break;
+            }
+            std::this_thread::yield();
+        }
+    }
+
     // return a future, calling future.get() will wait task done and return RetType.
     // commit(fn, args...)
     // commit(std::bind(&Class::mem_fn, &obj))
@@ -99,31 +108,31 @@ class HThreadPool {
             std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));
         std::future<RetType> future = task->get_future();
         {
-            std::lock_guard<std::mutex> locker(mutex);
+            std::lock_guard<std::mutex> locker(_mutex);
             tasks.emplace([task]{
                 (*task)();
             });
         }
 
-        cond.notify_one();
+        _cond.notify_one();
         return future;
     }
 
- public:
-    int pool_size;
-    std::atomic<int> idle_num;
-
+public:
     enum Status {
         STOP,
         RUNNING,
         PAUSE,
     };
+    int                 pool_size;
     std::atomic<Status> status;
-    std::vector<std::thread> workers;
+    std::atomic<int>    idle_num;
+    std::vector<std::thread>    workers;
+    std::queue<Task>            tasks;
 
-    std::queue<Task> tasks;
-    std::mutex        mutex;
-    std::condition_variable cond;
+protected:
+    std::mutex              _mutex;
+    std::condition_variable _cond;
 };
 
 #endif  // HW_THREAD_POOL_H_

+ 2 - 0
h.h

@@ -12,6 +12,7 @@
 #include "hversion.h"
 
 // c
+#include "hbase.h"
 #include "hsysinfo.h"
 #include "hproc.h"
 #include "hmath.h"
@@ -29,6 +30,7 @@
 #include "hgui.h"
 #include "hbuf.h"
 #include "hfile.h"
+#include "hdir.h"
 #include "hscope.h"
 #include "hthread.h"
 #include "hthreadpool.h"

+ 29 - 0
unittest/connect_test.c

@@ -0,0 +1,29 @@
+#include "hsocket.h"
+#include "htime.h"
+
+int main(int argc, char* argv[]) {
+    if (argc < 3) {
+        printf("Usage: cmd ip port\n");
+        return -10;
+    }
+
+    const char* ip = argv[1];
+    int port = atoi(argv[2]);
+
+    uint64_t start_time = gethrtime();
+    int ret = ConnectNonblock(ip, port);
+    uint64_t end_time = gethrtime();
+    printf("ConnectNonblock[%s:%d] retval=%d cost=%luus\n", ip, port, ret, end_time-start_time);
+
+    start_time = gethrtime();
+    ret = ConnectTimeout(ip, port, 3000);
+    end_time = gethrtime();
+    printf("ConnectTimeout[%s:%d] retval=%d cost=%luus\n", ip, port, ret, end_time-start_time);
+
+    start_time = gethrtime();
+    ret = Connect(ip, port, 0);
+    end_time = gethrtime();
+    printf("ConnectBlock[%s:%d] retval=%d cost=%luus\n", ip, port, ret, end_time-start_time);
+
+    return 0;
+};

+ 19 - 0
unittest/defer_test.cpp

@@ -0,0 +1,19 @@
+#include <stdio.h>
+
+#include "hscope.h"
+
+int main() {
+    defer (
+        printf("1\n");
+        printf("2\n");
+    )
+
+    defer (
+        printf("3\n");
+        printf("4\n");
+    )
+
+    defer(printf("hello\n");)
+
+    return 0;
+}

+ 16 - 0
unittest/ifconfig_test.cpp

@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+#include "ifconfig.h"
+
+int main() {
+    std::vector<ifconfig_t> ifcs;
+    ifconfig(ifcs);
+    for (auto& item : ifcs) {
+        printf("%s\nip: %s\nmask: %s\nmac: %s\n\n",
+                item.name,
+                item.ip,
+                item.mask,
+                item.mac);
+    }
+    return 0;
+}

+ 34 - 0
unittest/listdir_test.cpp

@@ -0,0 +1,34 @@
+#include <stdio.h>
+
+#include "hdir.h"
+
+int main(int argc, char* argv[]) {
+    const char* dir = ".";
+    if (argc > 1) {
+        dir = argv[1];
+    }
+    std::list<hdir_t> dirs;
+    listdir(dir, dirs);
+    for (auto& item : dirs) {
+        printf("%c\t", item.type);
+        float hsize;
+        if (item.size < 1024) {
+            printf("%lu\t", item.size);
+        }
+        else if ((hsize = item.size/1024.0f) < 1024.0f) {
+            printf("%.1fK\t", hsize);
+        }
+        else if ((hsize /= 1024.0f) < 1024.0f) {
+            printf("%.1fM\t", hsize);
+        }
+        else {
+            hsize /= 1024.0f;
+            printf("%.1fG\t", hsize);
+        }
+        struct tm* tm = localtime(&item.mtime);
+        printf("%04d-%02d-%02d %02d:%02d:%02d\t",
+                tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+        printf("%s%s\n", item.name, item.type == 'd' ? "/" : "");
+    }
+    return 0;
+}

+ 0 - 0
examples/ping.c → unittest/ping_test.c


+ 29 - 0
unittest/threadpool_test.cpp

@@ -0,0 +1,29 @@
+#include <stdio.h>
+
+#include "hthreadpool.h"
+#include "htime.h"
+
+void print_task(int i) {
+    printf("thread[%x]: task[%d]\n", gettid(), i);
+    sleep(1);
+}
+
+int main(int argc, char** argv) {
+    HThreadPool tp(4);
+    tp.start();
+
+    int i = 0;
+    for (; i < 10; ++i) {
+        tp.commit(print_task, i);
+    }
+
+    tp.wait();
+
+    for (; i < 20; ++i) {
+        tp.commit(print_task, i);
+    }
+
+    tp.wait();
+
+    return 0;
+}

+ 2 - 1
examples/webbench.c → unittest/webbench.c

@@ -261,7 +261,8 @@ int main(int argc, char** argv) {
     FILE* fp = NULL;
     int succeed = 0, failed = 0, bytes = 0;
     int childs = clients;
-    for (int i = 0; i < childs; ++i) {
+    int i;
+    for (i = 0; i < childs; ++i) {
         pid = fork();
         if (pid < 0) {
             perror("fork");