├── third_party └── CxxJDK ├── img └── benchmark.gif ├── test ├── main.cpp ├── certs │ ├── tests-cert.pem │ ├── ca-cert.pem │ └── tests-key.pem ├── Makefile_unix ├── benchmark.cpp ├── echoserver.cpp └── testeco.cpp ├── CHANGELOG.md ├── inc ├── EFiberCondition.hh ├── EFiberTimer.hh ├── EFiberMutex.hh ├── EFiberBlocker.hh ├── EFiberChannel.hh ├── EFiberLocal.hh ├── EFiberDebugger.hh ├── EFiberUtil.hh ├── EFiber.hh └── EFiberScheduler.hh ├── Eco.hh ├── src ├── EFiberTimer.cpp ├── elf_hook.h ├── EFiberCondition.cpp ├── EHooker.hh ├── EFiberDebugger.cpp ├── EFiberMutex.cpp ├── EContext.hh ├── mach_hook.h ├── EIoWaiter.hh ├── EFileContext.hh ├── eco_ae_select.c ├── EIoWaiter.cpp ├── EFileContext.cpp ├── EFiberBlocker.cpp ├── EFiber.cpp ├── mach_hook.c ├── EContext.cpp ├── eco_ae_kqueue.c ├── eco_ae_epoll.c ├── eco_ae.h ├── elf_hook.c ├── eco_ae.c └── EFiberScheduler.cpp ├── README.zh_cn.md ├── README.md └── LICENSE /third_party/CxxJDK: -------------------------------------------------------------------------------- 1 | ../../CxxJDK -------------------------------------------------------------------------------- /img/benchmark.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cxxjava/CxxFiber/HEAD/img/benchmark.gif -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include "es_main.h" 2 | 3 | int main(int argc, const char **argv) { 4 | 5 | // MAIN_CALL(testeco); 6 | 7 | MAIN_CALL(testeco_benchmark); 8 | 9 | // MAIN_CALL(eco_echo_server); 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2018/10/26 2 | 3 | 1. 添加对动态链接库的hook支持; 4 | 2. 修正bugs; 5 | 6 | 7 | 8 | 2018/06/02 9 | 10 | 1. 修正kqueue api hook 在仅EV_SET操作时的错误; 11 | 2. 优化c++11下hook性能; 12 | 13 | 14 | 15 | 2017/08/24 16 | 17 | 1. 修正hook时使用va_list错误; 18 | 19 | 20 | 21 | 2017/05/08 22 | 23 | 1. 同步到同期最新版的CxxJDK; 24 | 2. 添加调度侦听功能(@see:setScheduleCallback); 25 | 3. 添加多线程调度自定义负载均衡功能(@see:setBalanceCallback); 26 | 4. 添加对sendfile()的hook支持; -------------------------------------------------------------------------------- /inc/EFiberCondition.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberCondition.hh 3 | * 4 | * Created on: 2016-5-24 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERCONDITION_HH_ 9 | #define EFIBERCONDITION_HH_ 10 | 11 | #include "./EFiberBlocker.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * 18 | */ 19 | 20 | class EFiberCondition: public EObject { 21 | public: 22 | void await(); 23 | void signal(); 24 | void signalAll(); 25 | 26 | private: 27 | EConcurrentLinkedQueue waiters; 28 | }; 29 | 30 | } /* namespace eco */ 31 | } /* namespace efc */ 32 | #endif /* EFIBERCONDITION_HH_ */ 33 | -------------------------------------------------------------------------------- /Eco.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * Eco.hh 3 | * 4 | * Created on: 2016-10-01 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef __ECO_HH__ 9 | #define __ECO_HH__ 10 | 11 | #define ECO_VERSION "0.4.2" 12 | 13 | #include "Efc.hh" 14 | 15 | //eco 16 | #include "./inc/EFiber.hh" 17 | #include "./inc/EFiberTimer.hh" 18 | #include "./inc/EFiberScheduler.hh" 19 | #include "./inc/EFiberBlocker.hh" 20 | #include "./inc/EFiberMutex.hh" 21 | #include "./inc/EFiberCondition.hh" 22 | #include "./inc/EFiberChannel.hh" 23 | #include "./inc/EFiberLocal.hh" 24 | #include "./inc/EFiberDebugger.hh" 25 | 26 | using namespace efc::eco; 27 | 28 | #endif // !__ECO_HH__ 29 | -------------------------------------------------------------------------------- /inc/EFiberTimer.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberTimer.hh 3 | * 4 | * Created on: 2016-5-21 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERTIMER_HH_ 9 | #define EFIBERTIMER_HH_ 10 | 11 | #include "./EFiber.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * 18 | */ 19 | 20 | abstract class EFiberTimer: public ERunnable { 21 | public: 22 | 23 | /** 24 | * 25 | */ 26 | void cancel(); 27 | 28 | private: 29 | friend class TimberFiber; 30 | 31 | wp fiber; 32 | 33 | void setFiber(EFiber* fiber); 34 | }; 35 | 36 | } /* namespace eco */ 37 | } /* namespace efc */ 38 | #endif /* EFIBERTIMER_HH_ */ 39 | -------------------------------------------------------------------------------- /src/EFiberTimer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberTimer.cpp 3 | * 4 | * Created on: 2016-5-21 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "../inc/EFiberTimer.hh" 9 | #include "../inc/EFiberScheduler.hh" 10 | 11 | namespace efc { 12 | namespace eco { 13 | 14 | void EFiberTimer::cancel() { 15 | EFiberScheduler* fs = EFiberScheduler::currentScheduler(); 16 | if (fs) { 17 | sp fiber = this->fiber.lock(); 18 | if (fiber != null) { 19 | fiber->cancel(); 20 | } 21 | } 22 | } 23 | 24 | void EFiberTimer::setFiber(EFiber* fiber) { 25 | this->fiber = fiber->shared_from_this(); 26 | } 27 | 28 | } /* namespace eco */ 29 | } /* namespace efc */ 30 | -------------------------------------------------------------------------------- /src/elf_hook.h: -------------------------------------------------------------------------------- 1 | #ifdef __linux__ 2 | 3 | //@see: https://github.com/shoumikhin/ELF-Hook 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | 15 | /* 16 | * A structure representing a particular intended rebinding from a symbol 17 | * name to its replacement 18 | */ 19 | struct rebinding { 20 | const char *name; 21 | void *replacement; 22 | void **replaced; 23 | }; 24 | 25 | /* 26 | * Rebinds as above, but only in the specified image. 27 | */ 28 | int rebind_symbols_image(char const *library_filename, void *handle, 29 | struct rebinding rebindings[], size_t rebindings_nel); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif //#!__linux__ 36 | -------------------------------------------------------------------------------- /src/EFiberCondition.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberCondition.cpp 3 | * 4 | * Created on: 2016-5-24 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "../inc/EFiberCondition.hh" 9 | 10 | namespace efc { 11 | namespace eco { 12 | 13 | void EFiberCondition::await() { 14 | sp fb = new EFiberBlocker(0); 15 | waiters.add(fb); 16 | fb->wait(); 17 | } 18 | 19 | void EFiberCondition::signal() { 20 | sp fb = waiters.poll(); 21 | if (fb != null) { 22 | fb->wakeUp(); 23 | } 24 | } 25 | 26 | void EFiberCondition::signalAll() { 27 | sp > > iter = waiters.iterator(); 28 | while (iter->hasNext()) { 29 | sp fb = iter->next(); 30 | fb->wakeUp(); 31 | } 32 | } 33 | 34 | } /* namespace eco */ 35 | } /* namespace efc */ 36 | -------------------------------------------------------------------------------- /src/EHooker.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EHooker.hh 3 | * 4 | * Created on: 2016-5-12 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EHOOKER_HH_ 9 | #define EHOOKER_HH_ 10 | 11 | #include "Efc.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * 18 | */ 19 | 20 | class EHooker { 21 | public: 22 | DECLARE_STATIC_INITZZ 23 | 24 | public: 25 | static boolean isInterrupted(); 26 | static llong interruptEscapedTime(); 27 | 28 | #ifdef CPP11_SUPPORT 29 | template 30 | static ssize_t comm_io_on_fiber(F fn, const char* name, int event, int fd, Args&&... args); 31 | #else 32 | template 33 | static ssize_t comm_io_on_fiber(F fn, const char* name, int event, int fd, ...); 34 | #endif 35 | }; 36 | 37 | } /* namespace eco */ 38 | } /* namespace efc */ 39 | #endif /* EHOOKER_HH_ */ 40 | -------------------------------------------------------------------------------- /src/EFiberDebugger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberDebugger.cpp 3 | * 4 | * Created on: 2016-5-20 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "../inc/EFiberDebugger.hh" 9 | 10 | namespace efc { 11 | namespace eco { 12 | 13 | #if USE_ELOG 14 | sp EFiberDebugger::logger = ELoggerManager::getLogger("eco"); 15 | #endif 16 | 17 | EFiberDebugger::EFiberDebugger(): debugFlags(0) { 18 | } 19 | 20 | void EFiberDebugger::debugOn(int flags) { 21 | debugFlags |= flags; 22 | } 23 | 24 | void EFiberDebugger::debugOff(int flags) { 25 | debugFlags &= ~flags; 26 | } 27 | 28 | boolean EFiberDebugger::isDebugOn(int flags) { 29 | return (debugFlags & flags); 30 | } 31 | 32 | EFiberDebugger& EFiberDebugger::getInstance() { 33 | static EFiberDebugger debugger; 34 | return debugger; 35 | } 36 | 37 | } /* namespace eco */ 38 | } /* namespace efc */ 39 | -------------------------------------------------------------------------------- /inc/EFiberMutex.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberMutex.hh 3 | * 4 | * Created on: 2016-5-9 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERMUTEX_HH_ 9 | #define EFIBERMUTEX_HH_ 10 | 11 | #include "./EFiberBlocker.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * Mutex for fiber. 18 | */ 19 | 20 | class EFiberMutex: public ELock { 21 | public: 22 | virtual ~EFiberMutex(); 23 | EFiberMutex(); 24 | 25 | virtual void lock(); 26 | virtual void lockInterruptibly() THROWS(EUnsupportedOperationException); 27 | virtual boolean tryLock(); 28 | virtual boolean tryLock(llong time, ETimeUnit* unit=ETimeUnit::MILLISECONDS); 29 | virtual void unlock(); 30 | virtual ECondition* newCondition() THROWS(EUnsupportedOperationException); 31 | 32 | virtual boolean isLocked(); 33 | 34 | private: 35 | EFiberBlocker blocker; 36 | }; 37 | 38 | } /* namespace eco */ 39 | } /* namespace efc */ 40 | #endif /* EFIBERMUTEX_HH_ */ 41 | -------------------------------------------------------------------------------- /inc/EFiberBlocker.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberBlocker.hh 3 | * 4 | * Created on: 2016-5-9 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERBLOCKER_HH_ 9 | #define EFIBERBLOCKER_HH_ 10 | 11 | #include "Efc.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * 18 | */ 19 | 20 | class EFiber; 21 | 22 | class EFiberBlocker: public EObject { 23 | public: 24 | virtual ~EFiberBlocker(); 25 | 26 | EFiberBlocker(uint capacity=0, uint limit=-1); 27 | 28 | void wait(); 29 | boolean tryWait(); 30 | boolean tryWait(llong time, ETimeUnit* unit=ETimeUnit::MILLISECONDS); 31 | boolean wakeUp(); 32 | boolean isWaking(); 33 | 34 | private: 35 | friend class EFiberScheduler; 36 | 37 | class SyncObj: public EQueueEntry, public ESynchronizeable { 38 | }; 39 | 40 | uint wakeup_; 41 | uint limit_; 42 | 43 | EConcurrentIntrusiveDeque waitQueue; 44 | sp sync_; 45 | 46 | boolean swapOut(EFiber* fiber); 47 | }; 48 | 49 | } /* namespace eco */ 50 | } /* namespace efc */ 51 | #endif /* EFIBERBLOCKER_HH_ */ 52 | -------------------------------------------------------------------------------- /src/EFiberMutex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberMutex.cpp 3 | * 4 | * Created on: 2016-5-9 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "../inc/EFiberMutex.hh" 9 | #include "../inc/EFiber.hh" 10 | 11 | namespace efc { 12 | namespace eco { 13 | 14 | EFiberMutex::~EFiberMutex() { 15 | } 16 | 17 | EFiberMutex::EFiberMutex(): blocker(1, 1) { 18 | } 19 | 20 | void EFiberMutex::lock() { 21 | blocker.wait(); 22 | } 23 | 24 | void EFiberMutex::lockInterruptibly() { 25 | throw EUnsupportedOperationException(__FILE__, __LINE__); 26 | } 27 | 28 | boolean EFiberMutex::tryLock() { 29 | return blocker.tryWait(); 30 | } 31 | 32 | boolean EFiberMutex::tryLock(llong time, ETimeUnit* unit) { 33 | return blocker.tryWait(time, unit); 34 | } 35 | 36 | void EFiberMutex::unlock() { 37 | blocker.wakeUp(); 38 | } 39 | 40 | ECondition* EFiberMutex::newCondition() { 41 | throw EUnsupportedOperationException(__FILE__, __LINE__); 42 | } 43 | 44 | boolean EFiberMutex::isLocked() { 45 | return !blocker.isWaking(); 46 | } 47 | 48 | } /* namespace eco */ 49 | } /* namespace efc */ 50 | -------------------------------------------------------------------------------- /test/certs/tests-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKzCCAhOgAwIBAgIBCjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEP 3 | MA0GA1UECgwGVGhyaWZ0MSUwIwYDVQQDDBxUaHJpZnQgQ2VydGlmaWNhdGUgQXV0 4 | aG9yaXR5MB4XDTE0MDUxNjIwMjg1MloXDTQxMTAwMTIwMjg1MlowRjELMAkGA1UE 5 | BhMCVVMxDTALBgNVBAgTBE9oaW8xETAPBgNVBAcTCEhpbGxpYXJkMRUwEwYDVQQD 6 | EwxBc294IENvbXBhbnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz 7 | ZGrJ5XQHAuMYHlBgn32OOc9l0n3RjXccio2ceeWctXkSxDP3vFyZ4kBILF1lsY1g 8 | o8UTjMkqSDYcytCLK0qavrv9BZRLB9FcpqJ9o4V9feaI/HsHa8DYHyEs8qyNTTNG 9 | YQ3i4j+AA9iDSpezIYy/tyAOAjrSquUW1jI4tzKTBh8hk8MAMvR2/NPHPkrp4gI+ 10 | EMH6u4vWdr4F9bbriLFWoU04T9mWOMk7G+h8BS9sgINg2+v5cWvl3BC4kLk5L1yJ 11 | FEyuofSSCEEe6dDf7uVh+RPKa4hEkIYo31AEOPFrN56d+pCj/5l67HTWXoQx3rjy 12 | dNXMvgU75urm6TQe8dB5AgMBAAGjJTAjMCEGA1UdEQQaMBiHBH8AAAGHEAAAAAAA 13 | AAAAAAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAD26XYInaEvlWZJYgtl3yQyC 14 | 3NRQc3LG7XxWg4aFdXCxYLPRAL2HLoarKYH8GPFso57t5xnhA8WfP7iJxmgsKdCS 15 | 0pNIicOWsMmXvYLib0j9tMCFR+a8rn3f4n+clwnqas4w/vWBJUoMgyxtkP8NNNZO 16 | kIl02JKRhuyiFyPLilVp5tu0e+lmyUER+ak53WjLq2yoytYAlHkzkOpc4MZ/TNt5 17 | UTEtx/WVlZvlrPi3dsi7QikkjQgo1wCnm7owtuAHlPDMAB8wKk4+vvIOjsGM33T/ 18 | 8ffq/4X1HeYM0w0fM+SVlX1rwkXA1RW/jn48VWFHpWbE10+m196OdiToGfm2OJI= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /src/EContext.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EContext.hh 3 | * 4 | * Created on: 2016-5-1 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef ECONTEXT_HH_ 9 | #define ECONTEXT_HH_ 10 | 11 | #ifdef __APPLE__ 12 | //@see: "error ucontext routines are deprecated, and require _XOPEN_SOURCE to be defined" 13 | //@see: http://boost-users.boost.narkive.com/oBZ9TxFN/coroutine-bus-error-on-mac-os-x-10-5 14 | #define _XOPEN_SOURCE 15 | #endif 16 | 17 | #include "Efc.hh" 18 | 19 | #include 20 | 21 | namespace efc { 22 | namespace eco { 23 | 24 | /** 25 | * 26 | */ 27 | 28 | class EFiber; 29 | 30 | class EContext { 31 | public: 32 | virtual ~EContext(); 33 | 34 | EContext(EFiber* fiber); 35 | 36 | boolean swapIn(); 37 | boolean swapOut(); 38 | 39 | static inline void* getOrignContext(); 40 | static void cleanOrignContext(); 41 | 42 | private: 43 | friend class EFiber; 44 | 45 | #ifdef __x86_64__ 46 | es_uint64_t env[10]; 47 | #endif 48 | ucontext_t* context; 49 | 50 | EFiber* fiber; 51 | char* stackAddr; /* Base of stack's allocated memory */ 52 | int errno_; /* Global errno */ 53 | 54 | static EThreadLocalStorage threadLocal; 55 | 56 | static void fiber_worker(void* arg); 57 | }; 58 | 59 | } /* namespace eco */ 60 | } /* namespace efc */ 61 | #endif /* ECONTEXT_HH_ */ 62 | -------------------------------------------------------------------------------- /test/certs/ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAKMZICGWUzawMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZUaHJpZnQxJTAjBgNVBAMMHFRocmlmdCBDZXJ0aWZp 4 | Y2F0ZSBBdXRob3JpdHkwHhcNMTQwNTE2MjAyODUyWhcNNDExMDAxMjAyODUyWjBF 5 | MQswCQYDVQQGEwJVUzEPMA0GA1UECgwGVGhyaWZ0MSUwIwYDVQQDDBxUaHJpZnQg 6 | Q2VydGlmaWNhdGUgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEA1Bx2vUvXZ8PrvEBxwdH5qM1F2Xo7UkeC1jzQ+OLUBEcCiEduyStitSvB 8 | NOAzAGdjt7NmHTP/7OJngp2vzQGjSQzm20XacyTieFUuPBuikUc0Ge3Tf+uQXtiU 9 | zZPh+xn6arHH+zBWtmUCt3cBrpgRqdnWUsbl8eqo5HsczY781FxQbDoT9VP6A+9R 10 | KGTsEhxxKbWJ1C7OngwLKc7Zv4DtTC1JFlFyKd8ryDtxP4s/GgsXJkoK0Hkpputr 11 | cMxMm6OGt77mFvzR2qRY1CpEK/9rjBB6Gqd8GakXsvoOsqL/37k2wVhN/JoS/Pde 12 | 12Mp6TZ2rA8NW8vRujfWU0u55gnQnwIDAQABo1AwTjAdBgNVHQ4EFgQUQ00NGVmY 13 | NZ6LJg8UQUOVLZX1Gh8wHwYDVR0jBBgwFoAUQ00NGVmYNZ6LJg8UQUOVLZX1Gh8w 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAdlxt5+z9uXCBr1Wt6r49 15 | 4MmOYw9lOnEOG1JPMRo108TLpmwXEWReCAtjQuR7BitRJW0kJtlO1M6t3qoIh6GA 16 | sBkgsjQM1xNY3YEpx71MLt1V+JD+2WtSBKMyysj1TiOmIH66kkvXO3ptXzhjhZyX 17 | G6B+kxLtxrqkn9SJULyN55X8T+dkW28UIBZVLavoREDU+UPrYU9JgZeIVObtGSWi 18 | DvS4RIJZNjgG3vTrT00rfUGEfTlI54Vbcmv0cYvswP/nMsLtDStCdgI7c/ipyJve 19 | dfuI4CedjE240AxK5OFxFg/k/IfnB4a5oojbdIR9hKrTU57TPaUVD50Na9WA1aqX 20 | 5Q== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /src/mach_hook.h: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | 3 | //@see: https://github.com/facebook/fishhook/ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __LP64__ 15 | typedef struct mach_header_64 mach_header_t; 16 | typedef struct segment_command_64 segment_command_t; 17 | typedef struct section_64 section_t; 18 | typedef struct nlist_64 nlist_t; 19 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 20 | #else 21 | typedef struct mach_header mach_header_t; 22 | typedef struct segment_command segment_command_t; 23 | typedef struct section section_t; 24 | typedef struct nlist nlist_t; 25 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 26 | #endif 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /* 33 | * A structure representing a particular intended rebinding from a symbol 34 | * name to its replacement 35 | */ 36 | struct rebinding { 37 | const char *name; 38 | void *replacement; 39 | void **replaced; 40 | }; 41 | 42 | /* 43 | * Rebinds as above, but only in the specified image. The header should point 44 | * to the mach-o header, the slide should be the slide offset. Others as above. 45 | */ 46 | int rebind_symbols_image(void *header, intptr_t slide, 47 | struct rebinding rebindings[], size_t rebindings_nel); 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif //!__APPLE__ 54 | -------------------------------------------------------------------------------- /inc/EFiberChannel.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberChannel.hh 3 | * 4 | * Created on: 2016-5-5 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBER_CHANNEL_HH_ 9 | #define EFIBER_CHANNEL_HH_ 10 | 11 | #include "./EFiber.hh" 12 | #include "./EFiberBlocker.hh" 13 | 14 | namespace efc { 15 | namespace eco { 16 | 17 | /** 18 | * 19 | */ 20 | 21 | template 22 | class EFiberChannel: public EObject { 23 | public: 24 | virtual ~EFiberChannel() { 25 | } 26 | 27 | EFiberChannel(int capacity = 0): writer(capacity) { 28 | } 29 | 30 | sp read() { 31 | writer.wakeUp(); 32 | reader.wait(); 33 | return dataQueue.poll(); 34 | } 35 | 36 | void write(sp o) { 37 | writer.wait(); 38 | dataQueue.add(o); 39 | reader.wakeUp(); 40 | } 41 | 42 | boolean tryRead(sp& o) { 43 | writer.wakeUp(); 44 | while (!reader.tryWait()) { 45 | if (writer.tryWait()) { 46 | return false; 47 | } else { 48 | EFiber::yield(); 49 | } 50 | } 51 | o = dataQueue.poll(); 52 | return true; 53 | } 54 | 55 | boolean tryWrite(sp o) { 56 | if (!writer.tryWait()) 57 | return false; 58 | dataQueue.add(o); 59 | reader.wakeUp(); 60 | return true; 61 | } 62 | 63 | boolean isEmpty() { 64 | return !reader.isWaking(); 65 | } 66 | 67 | private: 68 | EConcurrentLiteQueue dataQueue; 69 | EFiberBlocker reader; 70 | EFiberBlocker writer; 71 | }; 72 | 73 | } /* namespace eco */ 74 | } /* namespace efc */ 75 | #endif /* EFIBER_CHANNEL_HH_ */ 76 | -------------------------------------------------------------------------------- /src/EIoWaiter.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EIoWaiter.hh 3 | * 4 | * Created on: 2016-5-10 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EIOWAITER_HH_ 9 | #define EIOWAITER_HH_ 10 | 11 | #include "Efc.hh" 12 | #include "../inc/EFiber.hh" 13 | #include "eco_ae.h" 14 | 15 | namespace efc { 16 | namespace eco { 17 | 18 | /** 19 | * 20 | */ 21 | 22 | class EIoWaiter { 23 | public: 24 | virtual ~EIoWaiter(); 25 | 26 | EIoWaiter(int iosetSize); 27 | 28 | /** 29 | * 30 | */ 31 | void loopProcessEvents(); 32 | int onceProcessEvents(int timeout=0); 33 | 34 | /** 35 | * 36 | */ 37 | void setFileEvent(int fd, int mask, sp fiber); 38 | void delFileEvent(int fd, int mask); 39 | int getFileEvent(int fd); 40 | 41 | /** 42 | * 43 | */ 44 | llong setupTimer(llong timeout, sp fiber); 45 | void cancelTimer(llong id); 46 | 47 | /** 48 | * 49 | */ 50 | int getNativePollHandle(); 51 | int getWaitersCount(); 52 | 53 | /** 54 | * 55 | */ 56 | void interrupt(); 57 | 58 | /** 59 | * 60 | */ 61 | boolean swapOut(sp& fiber); 62 | 63 | /** 64 | * 65 | */ 66 | void signal(); 67 | 68 | private: 69 | co_poll_t* poll; 70 | es_pipe_t* pipe; 71 | int waiters; 72 | 73 | static void fileEventProc(co_poll_t *poll, int fd, void *clientData, int mask); 74 | static int timeEventProc(co_poll_t *poll, llong id, void *clientData); 75 | static void timeEventFinalizerProc(co_poll_t *poll, void *clientData); 76 | }; 77 | 78 | } /* namespace eco */ 79 | } /* namespace efc */ 80 | #endif /* EIOWAITER_HH_ */ 81 | -------------------------------------------------------------------------------- /inc/EFiberLocal.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberLocal.hh 3 | * 4 | * Created on: 2016-5-24 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERLOCAL_HH_ 9 | #define EFIBERLOCAL_HH_ 10 | 11 | #include "./EFiber.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * 18 | */ 19 | 20 | typedef void (*fiber_destroyed_callback_t)(void* data); 21 | 22 | struct EFiberLocalKeyWrap { 23 | EFiberLocalKeyWrap(fiber_destroyed_callback_t cb): callback(cb) {} 24 | fiber_destroyed_callback_t callback; 25 | }; 26 | 27 | template 28 | class EFiberLocal: public EObject { 29 | public: 30 | EFiberLocal(): keyWrap(null) { 31 | } 32 | EFiberLocal(fiber_destroyed_callback_t cb): keyWrap(cb) { 33 | } 34 | 35 | E get() { 36 | EFiber* currFiber = EFiber::currentFiber(); 37 | if (!currFiber) { 38 | return null; 39 | } 40 | return (E)(eso_hash_get(currFiber->localValues, &keyWrap, sizeof(this))); 41 | } 42 | 43 | E set(E e) { 44 | EFiber* currFiber = EFiber::currentFiber(); 45 | if (!currFiber) { 46 | return null; 47 | } 48 | return (E)(eso_hash_set(currFiber->localValues, &keyWrap, sizeof(this), e)); 49 | } 50 | 51 | E remove() { 52 | EFiber* currFiber = EFiber::currentFiber(); 53 | if (!currFiber) { 54 | return null; 55 | } 56 | return (E)(eso_hash_set(currFiber->localValues, &keyWrap, sizeof(this), NULL)); 57 | } 58 | 59 | private: 60 | friend class EFiber; 61 | 62 | EFiberLocalKeyWrap keyWrap; 63 | }; 64 | 65 | } /* namespace eco */ 66 | } /* namespace efc */ 67 | #endif /* EFIBERLOCAL_HH_ */ 68 | -------------------------------------------------------------------------------- /src/EFileContext.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFileContext.hh 3 | * 4 | * Created on: 2016-5-14 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFILECONTEXT_HH_ 9 | #define EFILECONTEXT_HH_ 10 | 11 | #include "Efc.hh" 12 | #include 13 | 14 | namespace efc { 15 | namespace eco { 16 | 17 | /** 18 | * 19 | */ 20 | 21 | class EFileContext: public EObject { 22 | public: 23 | virtual ~EFileContext(); 24 | 25 | EFileContext(int fd); 26 | 27 | int configureNonBlocking(); 28 | int inFD(); 29 | 30 | void setSysNonBlock(boolean nonblock); 31 | boolean isSysNonBlocked(); 32 | void setUserNonBlock(boolean nonblock); 33 | boolean isUserNonBlocked(); 34 | 35 | void setRecvTimeout(const timeval* recvTimeout); 36 | int getRecvTimeout(); 37 | void setSendTimeout(const timeval* sendTimeout); 38 | int getSendTimeout(); 39 | 40 | static boolean isStreamFile(int fd); 41 | static boolean isSocketFile(int fd); 42 | 43 | private: 44 | int fd; 45 | boolean sysNonBlocked; 46 | boolean userNonBlocked; 47 | int recvTimeout; 48 | int sendTimeout; 49 | }; 50 | 51 | //============================================================================= 52 | 53 | /** 54 | * 55 | */ 56 | 57 | /** 58 | * (FD_DEFAULT_CHUNKS * FD_CHUNK_CAPACITY) is the max fd value. 59 | */ 60 | #define FD_DEFAULT_CHUNKS 32 61 | #define FD_CHUNK_CAPACITY 32768 62 | 63 | class EFileContextManager { 64 | public: 65 | EFileContextManager(int maxfd); 66 | ~EFileContextManager(); 67 | 68 | sp get(int fd); 69 | void remove(int fd); 70 | void clear(); 71 | 72 | private: 73 | std::vector > > hookedFiles; 74 | }; 75 | 76 | } /* namespace eco */ 77 | } /* namespace efc */ 78 | #endif /* EFILECONTEXT_HH_ */ 79 | -------------------------------------------------------------------------------- /test/certs/tests-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAs2RqyeV0BwLjGB5QYJ99jjnPZdJ90Y13HIqNnHnlnLV5EsQz 3 | 97xcmeJASCxdZbGNYKPFE4zJKkg2HMrQiytKmr67/QWUSwfRXKaifaOFfX3miPx7 4 | B2vA2B8hLPKsjU0zRmEN4uI/gAPYg0qXsyGMv7cgDgI60qrlFtYyOLcykwYfIZPD 5 | ADL0dvzTxz5K6eICPhDB+ruL1na+BfW264ixVqFNOE/ZljjJOxvofAUvbICDYNvr 6 | +XFr5dwQuJC5OS9ciRRMrqH0kghBHunQ3+7lYfkTymuIRJCGKN9QBDjxazeenfqQ 7 | o/+Zeux01l6EMd648nTVzL4FO+bq5uk0HvHQeQIDAQABAoIBAQCSPcBYindF5/Kd 8 | jMjVm+9M7I/IYAo1tG9vkvvSngSy9bWXuN7sjF+pCyqAK7qP1mh8acWVJGYx0+BZ 9 | JHVRnp8Y+3hg0hWL/PmN4EICzjVakjJHZhwddpglF2uCKurD3jV4oFIjrXE6uOfe 10 | UAbO/wCwoWa+RM8TQkGzljYmyiGufCcXlgEKMNA7TIvbJ9TVx3VTCOQy6EjZ13jd 11 | M6X7byV/ZOFpZ2H0QV46LvZraw04riXQ/59gVmzizYdI+BwnxxapsCmalTJoV/Y0 12 | LMI2ylat4PTMVTxPF+ti7Nt+rUkkEx6kuiAgfc+bzE4BSD5X4wy3fdLVLccoxXYw 13 | 4N3fOuQhAoGBAOLrMhiSCrzXGjDWTbPrwzxXDO0qm+wURELi3N5SXIkKUdG2/In6 14 | wNdpXdvqblOm7SASgPf9KCwUSADrNw6R6nbfrrir5EHg66YydI/OW42QzJKcBUFh 15 | 5Q5na3fvoL/zRhsmh0gEymBg+OIfNel2LY69bl8aAko2y0R1kj7zb8X1AoGBAMph 16 | 9hlnkIBSw60+pKHaOqo2t/kihNNMFyfOgJvh8960eFeMDhMIXgxPUR8yaPX0bBMb 17 | bCdEJJ2pmq7zUBPvxVJLedwkGMhywElA8yYVh+S6x4Cg+lYo4spIjrHQ/WTvJkHB 18 | GrDskxdq80lbXjwRd0dPJZkxhKJec1o0n8S03Mn1AoGAGarK5taWGlgmYUHMVj6j 19 | vc6G6si4DFMaiYpJu2gLiYC+Un9lP2I6r+L+N+LjidjG16rgJazf/2Rn5Jq2hpJg 20 | uAODKuZekkkTvp/UaXPJDVFEooy9V3DwTNnL4SwcvbmRw35vLOlFzvMJE+K94WN5 21 | sbyhoGY7vhNGmL7HxREaIoUCgYEAwpteVWFz3yE2ziF9l7FMVh7V23go9zGk1n9I 22 | xhyJL26khbLEWeLi5L1kiTYlHdUSE3F8F2n8N6s+ddq79t/KA29WV6xSNHW7lvUg 23 | mk975CMC8hpZfn5ETjVlGXGYJ/Wa+QGiE9z5ODx8gt6cB/DXnLdrtRqbqrJeA7C0 24 | rScpY/0CgYBCC1QeuAiwWHOqQn3BwsZo9JQBTyT0QvDqLH/F+h9QbXep+4HvyAxG 25 | nTMNDtGyfyKGDaDUn5hyeU7Oxvzq0K9P+eZD3MjQeaMEg/++GPGUPmDUTqyb2UT8 26 | 5s0NIUobxfKnTD6IpgOIq7ffvVY6cKBMyuLmu/gSvscsbONHjKti3Q== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /inc/EFiberDebugger.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberDebugger.hh 3 | * 4 | * Created on: 2016-5-20 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERDEBUGGER_HH_ 9 | #define EFIBERDEBUGGER_HH_ 10 | 11 | #define USE_ELOG 0 // 0-printf | 1-CxxLog4j 12 | 13 | #include "Efc.hh" 14 | #if USE_ELOG 15 | #include "ELog.hh" 16 | #endif 17 | 18 | namespace efc { 19 | namespace eco { 20 | 21 | /** 22 | * 23 | */ 24 | 25 | class EFiberDebugger { 26 | public: 27 | static const int NONE = 0X00; 28 | static const int FIBER = 0X01; 29 | static const int SCHEDULER = 0X02; 30 | static const int BLOCKER = 0X04; 31 | static const int WAITING = 0X08; 32 | static const int ALL = 0XFF; 33 | 34 | public: 35 | static EFiberDebugger& getInstance(); 36 | 37 | void debugOn(int flags); 38 | void debugOff(int flags); 39 | boolean isDebugOn(int flags); 40 | 41 | void log(const char* _file_, int _line_, const char* msg) { 42 | #if USE_ELOG 43 | logger->debug(_file_, _line_, msg); 44 | #else 45 | ECalendar cal(ESystem::currentTimeMillis(), ESystem::localTimeZone()); 46 | fprintf(stdout, "[%s][%s][%s:%d] %s\n", 47 | cal.toString("%Y%m%d %H:%M:%S,%s").c_str(), 48 | EThread::currentThread()->toString().c_str(), 49 | eso_filepath_name_get(_file_), _line_, 50 | msg); 51 | fflush(stdout); 52 | #endif 53 | } 54 | 55 | private: 56 | #if USE_ELOG 57 | static sp logger; 58 | #endif 59 | 60 | int debugFlags; 61 | EFiberDebugger(); 62 | }; 63 | 64 | #ifdef DEBUG 65 | #define ECO_DEBUG(type, fmt, ...) \ 66 | do { \ 67 | ::efc::eco::EFiberDebugger& debugger = ::efc::eco::EFiberDebugger::getInstance(); \ 68 | if (debugger.isDebugOn(type)) { \ 69 | EString s = EString::formatOf(fmt, ##__VA_ARGS__); \ 70 | debugger.log(__FILE__, __LINE__, s.c_str()); \ 71 | } \ 72 | } while(0) 73 | #else 74 | #define ECO_DEBUG(type, fmt, ...) 75 | #endif 76 | 77 | } /* namespace eco */ 78 | } /* namespace efc */ 79 | #endif /* EFIBERDEBUGGER_HH_ */ 80 | -------------------------------------------------------------------------------- /README.zh_cn.md: -------------------------------------------------------------------------------- 1 | # CxxFiber 2 | 3 | ## C++协程库. 4 | 5 | 6 | 7 | 英文版本: [English](README.md) 8 | 9 | 10 | 11 | ### 目录 12 | - [特点](#特点) 13 | - [示例](#示例) 14 | - [性能](#性能) 15 | - [依赖](#依赖) 16 | - [TODO](#todo) 17 | - [Support](#support) 18 | 19 | #### 特点 20 | * 跨平台:同时支持Linux32/64、OSX64两个平台,支持C++98; 21 | * 高性能:网络性能强劲,支持海量协程,从此单机1000K不再是问题; 22 | * 易开发:同步的方式编写代码,API优雅简洁、简单易用、开发高效; 23 | * 强融合:支持进程、线程、协程的混合使用,各司其职,充分发挥各自优点; 24 | 25 | #### 示例 26 | `c++:` 27 | 28 | ``` 29 | #include "Eco.hh" 30 | 31 | int main(int argc, const char **argv) { 32 | // CxxJDK init. 33 | ESystem::init(argc, argv); 34 | 35 | try { 36 | EFiberScheduler scheduler; 37 | 38 | scheduler.schedule([&](){ 39 | EServerSocket ss; 40 | ss.setReuseAddress(true); 41 | ss.bind(8888); 42 | 43 | while (!gStopFlag) { 44 | // accept 45 | sp socket = ss.accept(); 46 | if (socket != null) { 47 | // 创建新的协程 48 | scheduler.schedule([=](){ 49 | try { 50 | char buf[512] = {0}; 51 | // read 52 | EInputStream* is = socket->getInputStream(); 53 | int n = is->read(buf, sizeof(buf)); 54 | printf("read buf=%s\n", buf); 55 | 56 | // write 57 | EOutputStream* os = socket->getOutputStream(); 58 | os->write(buf, n); 59 | } catch (EIOException& e) { 60 | e.printStackTrace(); 61 | } catch (...) { 62 | } 63 | }); 64 | } 65 | } 66 | }); 67 | 68 | // 开始协程调度 69 | scheduler.join(); //单线程调度模式 70 | //scheduler.join(4); //多线程调度模式:参数4表示并发调度的线程数 71 | } 72 | catch (EException& e) { 73 | e.printStackTrace(); 74 | } 75 | catch (...) { 76 | printf("catch all...\n"); 77 | } 78 | 79 | ESystem::exit(0); 80 | 81 | return 0; 82 | } 83 | 84 | ``` 85 | 86 | 更多示例: 87 | [testeco.cpp](test/testeco.cpp) 88 | 89 | #### 性能 90 | `软件环境:` 91 | 92 | 见示例c++代码:[benchmark.cpp](test/benchmark.cpp); 93 | 94 | 95 | `硬件环境:` 96 | 97 | ``` 98 | 型号名称: MacBook Pro 99 | 型号标识符: MacBookPro10,2 100 | 处理器名称: Intel Core i5 101 | 处理器速度: 2.6 GHz 102 | 处理器数目: 1 103 | 核总数: 2 104 | ``` 105 | `测试结果:` 106 | 107 | ![benchmark](img/benchmark.gif) 108 | 109 | #### 依赖 110 | `CxxFiber`基于[CxxJDK](https://github.com/cxxjava/cxxjdk)开发. 111 | 112 | #### TODO 113 | 支持Win64平台。 114 | 115 | #### Support 116 | Email: [cxxjava@163.com](mailto:cxxjava@163.com) -------------------------------------------------------------------------------- /src/eco_ae_select.c: -------------------------------------------------------------------------------- 1 | /* Linux epoll(2) based ae.c module 2 | * 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifdef HAVE_SELECT 32 | 33 | 34 | static int aeApiCreate(aeEventLoop *eventLoop) { 35 | //TODO... 36 | return 0; 37 | } 38 | 39 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 40 | //TODO... 41 | return 0; 42 | } 43 | 44 | static void aeApiFree(aeEventLoop *eventLoop) { 45 | //TODO... 46 | return; 47 | } 48 | 49 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 50 | //TODO... 51 | return 0; 52 | } 53 | 54 | static int aeApiUpdEvent(aeEventLoop *eventLoop, int fd, int mask) { 55 | //TODO... 56 | return 0; 57 | } 58 | 59 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { 60 | //TODO... 61 | return; 62 | } 63 | 64 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 65 | //TODO... 66 | return 0; 67 | } 68 | 69 | static char *aeApiName(void) { 70 | return "select"; 71 | } 72 | 73 | static int aeApiHandle(aeEventLoop* eventLoop) { 74 | //TODO... 75 | return 0; 76 | } 77 | 78 | #endif //!HAVE_EPOLL 79 | -------------------------------------------------------------------------------- /test/Makefile_unix: -------------------------------------------------------------------------------- 1 | ################OPTION################### 2 | # release or debug 3 | VERTYPE=RELEASE 4 | 5 | KERNEL:=$(shell uname) 6 | LIBDIR = linux 7 | #CPPSTD = c++98 8 | CPPSTD = c++11 9 | 10 | ARCH:=$(shell uname -m) 11 | RC:=$(ARCH) 12 | BIT32:=i686 13 | BIT64:=x86_64 14 | 15 | $(info KERNEL=$(KERNEL)) 16 | $(info ARCH=$(ARCH)) 17 | 18 | ifeq ($(KERNEL),Darwin) 19 | LIBDIR = osx 20 | endif 21 | 22 | ifeq ($(RC),$(BIT32)) 23 | SHAREDLIB = -lefc32 -leso32 -lrt -lm -ldl -lpthread -lcrypto 24 | else 25 | SHAREDLIB = -lefc64 -leso64 -ldl -lpthread -lcrypto 26 | endif 27 | 28 | ifeq ($(VERTYPE), RELEASE) 29 | CCOMPILEOPTION = -c -g -O2 -D__MAIN__ 30 | CPPCOMPILEOPTION = -std=$(CPPSTD) -c -g -O2 -fpermissive -D__MAIN__ 31 | TESTECO = testeco 32 | BENCHMARK = benchmark 33 | ECHOSERVER = echoserver 34 | else 35 | CCOMPILEOPTION = -c -g -D__MAIN__ 36 | CPPCOMPILEOPTION = -std=$(CPPSTD) -c -g -fpermissive -DDEBUG -D__MAIN__ 37 | TESTECO = testeco_d 38 | BENCHMARK = benchmark_d 39 | ECHOSERVER = echoserver_d 40 | endif 41 | 42 | CCOMPILE = gcc 43 | CPPCOMPILE = g++ 44 | INCLUDEDIR = -I../../efc \ 45 | -I../../CxxJDK/efc \ 46 | -I../inc \ 47 | -I../ \ 48 | -I/usr/local/Cellar/openssl/1.0.2g/include \ 49 | 50 | LINK = g++ 51 | LINKOPTION = -std=$(CPPSTD) -g 52 | LIBDIRS = -L../../efc/lib/$(LIBDIR) -L../../CxxJDK/lib/$(LIBDIR) 53 | APPENDLIB = 54 | 55 | BASE_OBJS = \ 56 | ../src/EContext.o \ 57 | ../src/EFiber.o \ 58 | ../src/EFiberBlocker.o \ 59 | ../src/EFiberCondition.o \ 60 | ../src/EFiberDebugger.o \ 61 | ../src/EFiberMutex.o \ 62 | ../src/EFiberScheduler.o \ 63 | ../src/EFiberTimer.o \ 64 | ../src/EFileContext.o \ 65 | ../src/EHooker.o \ 66 | ../src/EIoWaiter.o \ 67 | ../src/eco_ae.o \ 68 | ../src/eco_ae_epoll.o \ 69 | ../src/eco_ae_kqueue.o \ 70 | 71 | TESTECO_OBJS = testeco.o \ 72 | 73 | BENCHMARK_OBJS = benchmark.o \ 74 | 75 | ECHOSERVER_OBJS = echoserver.o \ 76 | 77 | $(TESTECO): $(BASE_OBJS) $(TESTECO_OBJS) $(APPENDLIB) 78 | $(LINK) $(LINKOPTION) -o $(TESTECO) $(LIBDIRS) $(BASE_OBJS) $(TESTECO_OBJS) $(SHAREDLIB) $(APPENDLIB) 79 | 80 | $(BENCHMARK): $(BASE_OBJS) $(BENCHMARK_OBJS) $(APPENDLIB) 81 | $(LINK) $(LINKOPTION) -o $(BENCHMARK) $(LIBDIRS) $(BASE_OBJS) $(BENCHMARK_OBJS) $(SHAREDLIB) $(APPENDLIB) 82 | 83 | $(ECHOSERVER): $(BASE_OBJS) $(ECHOSERVER_OBJS) $(APPENDLIB) 84 | $(LINK) $(LINKOPTION) -o $(ECHOSERVER) $(LIBDIRS) $(BASE_OBJS) $(ECHOSERVER_OBJS) $(SHAREDLIB) $(APPENDLIB) 85 | 86 | clean: 87 | rm -f $(BASE_OBJS) $(TESTECO_OBJS) $(BENCHMARK_OBJS) $(ECHOSERVER_OBJS) 88 | 89 | all: clean $(TESTECO) $(BENCHMARK) $(ECHOSERVER) clean 90 | .PRECIOUS:%.cpp %.c 91 | .SUFFIXES: 92 | .SUFFIXES: .c .o .cpp 93 | 94 | .cpp.o: 95 | $(CPPCOMPILE) -c -o $*.o $(CPPCOMPILEOPTION) $(INCLUDEDIR) $*.cpp 96 | 97 | .c.o: 98 | $(CCOMPILE) -c -o $*.o $(CCOMPILEOPTION) $(INCLUDEDIR) $*.c 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CxxFiber 2 | 3 | ## C++ fiber library 4 | 5 | 6 | 7 | chinese version: [简体中文](README.zh_cn.md) 8 | 9 | 10 | 11 | ### Table of Contents 12 | - [Characteristics](#characteristics) 13 | - [Example](#example) 14 | - [Performance](#performance) 15 | - [Dependency](#dependency) 16 | - [TODO](#todo) 17 | - [Support](#support) 18 | 19 | #### Characteristics 20 | * Cross platform: support Linux32/64, OSX64, support C++98; 21 | * High performance: network performance is strong, support massive co process, since then no C1000K problem; 22 | * Easy development: synchronous code, API elegant simplicity and efficient development; 23 | * Mixed programming support process, threads and coroutines mixed use, carry out their duties: 24 | 25 | #### Example 26 | `c++:` 27 | 28 | ``` 29 | #include "Eco.hh" 30 | 31 | int main(int argc, const char **argv) { 32 | // CxxJDK init. 33 | ESystem::init(argc, argv); 34 | 35 | try { 36 | EFiberScheduler scheduler; 37 | 38 | scheduler.schedule([&](){ 39 | EServerSocket ss; 40 | ss.setReuseAddress(true); 41 | ss.bind(8888); 42 | 43 | while (!gStopFlag) { 44 | // accept 45 | sp socket = ss.accept(); 46 | if (socket != null) { 47 | // create a new fiber 48 | scheduler.schedule([=](){ 49 | try { 50 | char buf[512] = {0}; 51 | // read 52 | EInputStream* is = socket->getInputStream(); 53 | int n = is->read(buf, sizeof(buf)); 54 | printf("read buf=%s\n", buf); 55 | 56 | // write 57 | EOutputStream* os = socket->getOutputStream(); 58 | os->write(buf, n); 59 | } catch (EIOException& e) { 60 | e.printStackTrace(); 61 | } catch (...) { 62 | } 63 | }); 64 | } 65 | } 66 | }); 67 | 68 | // begin schedule 69 | scheduler.join(); //signal thread mode 70 | //scheduler.join(4); //multi thread mode: 4 is count of threads 71 | } 72 | catch (EException& e) { 73 | e.printStackTrace(); 74 | } 75 | catch (...) { 76 | printf("catch all...\n"); 77 | } 78 | 79 | ESystem::exit(0); 80 | 81 | return 0; 82 | } 83 | 84 | ``` 85 | 86 | more examples: 87 | [testeco.cpp](test/testeco.cpp) 88 | 89 | #### Performance 90 | `software environment:` 91 | 92 | @see c++ example code: [benchmark.cpp](test/benchmark.cpp); 93 | 94 | 95 | `hardware environment:` 96 | 97 | ``` 98 | Model Name: MacBook Pro 99 | Model Identifier: MacBookPro10,2 100 | Processor Speed: 2.6 GHz 101 | Number of Processors: 1 102 | Total Number of Cores: 2 103 | ``` 104 | `test results:` 105 | 106 | ![benchmark](img/benchmark.gif) 107 | 108 | #### Dependency 109 | `CxxFiber` is based on [CxxJDK](https://github.com/cxxjava/cxxjdk). 110 | 111 | #### TODO 112 | win64 support. 113 | 114 | #### Support 115 | Email: [cxxjava@163.com](mailto:cxxjava@163.com) 116 | 117 | -------------------------------------------------------------------------------- /inc/EFiberUtil.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberUtil.hh 3 | * 4 | * Created on: 2016-6-5 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERUTIL_HH_ 9 | #define EFIBERUTIL_HH_ 10 | 11 | #include "Efc.hh" 12 | 13 | #ifdef CPP11_SUPPORT 14 | #include 15 | #endif 16 | 17 | namespace efc { 18 | namespace eco { 19 | 20 | //============================================================================= 21 | 22 | #ifdef CPP11_SUPPORT 23 | class SpinLock { 24 | private: 25 | volatile std::atomic_flag lck; 26 | 27 | public: 28 | SpinLock() { 29 | lck.clear(); 30 | } 31 | 32 | ALWAYS_INLINE void lock() { 33 | while (std::atomic_flag_test_and_set_explicit(&lck, 34 | std::memory_order_acquire)) 35 | ; 36 | } 37 | 38 | ALWAYS_INLINE void unlock() { 39 | std::atomic_flag_clear_explicit(&lck, std::memory_order_release); 40 | } 41 | }; 42 | #else 43 | #define SpinLock ESpinLock 44 | #endif //!CPP11_SUPPORT 45 | 46 | //============================================================================= 47 | 48 | template 49 | class EFiberConcurrentQueue { 50 | public: 51 | typedef struct node_t { 52 | sp* volatile value; 53 | node_t* volatile next; 54 | 55 | node_t(): value(null), next(null) {} 56 | } NODE; 57 | 58 | public: 59 | ~EFiberConcurrentQueue() { 60 | delete head; 61 | } 62 | 63 | EFiberConcurrentQueue() { 64 | NODE* node = new NODE(); 65 | head = tail = node; 66 | } 67 | 68 | void add(sp* e) { 69 | NODE* node = (*e)->packing; 70 | node->value = e; 71 | node->next = null; 72 | tl.lock(); 73 | tail->next = node; 74 | tail = node; 75 | tl.unlock(); 76 | } 77 | 78 | sp* poll() { 79 | sp* v; 80 | NODE* node = null; 81 | hl.lock(); 82 | node = head; 83 | NODE* new_head = node->next; 84 | if (new_head == null) { 85 | hl.unlock(); 86 | return null; 87 | } 88 | v = new_head->value; 89 | head = new_head; 90 | hl.unlock(); 91 | { 92 | node->value = v; 93 | (*v)->packing = node; 94 | } 95 | return v; 96 | } 97 | 98 | private: 99 | NODE *head; 100 | NODE *tail; 101 | LOCK hl; 102 | LOCK tl; 103 | }; 104 | 105 | //============================================================================= 106 | 107 | #define LOCKFOR(p) SpinLockPool<0>::lockFor(p) 108 | 109 | #define POLLSIZE 41 110 | 111 | template< int I > class SpinLockPool 112 | { 113 | private: 114 | static SpinLock* pool_[POLLSIZE]; 115 | 116 | public: 117 | static SpinLock* lockFor(void const* pv) 118 | { 119 | es_size_t i = reinterpret_cast(pv) % POLLSIZE; 120 | return pool_[i]; 121 | } 122 | }; 123 | 124 | #define SPINLOCK_INIT new SpinLock() 125 | 126 | template< int I > SpinLock* SpinLockPool< I >::pool_[POLLSIZE] = 127 | { 128 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 129 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 130 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 131 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 132 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 133 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 134 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 135 | SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, SPINLOCK_INIT, 136 | SPINLOCK_INIT 137 | }; 138 | 139 | //============================================================================= 140 | 141 | } /* namespace eco */ 142 | } /* namespace efc */ 143 | #endif /* EFIBERUTIL_HH_ */ 144 | -------------------------------------------------------------------------------- /inc/EFiber.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiber.hh 3 | * 4 | * Created on: 2016-5-17 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBER_HH_ 9 | #define EFIBER_HH_ 10 | 11 | #include "./EFiberUtil.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | /** 17 | * 18 | */ 19 | 20 | class EContext; 21 | class EIoWaiter; 22 | class EFiberBlocker; 23 | class EFiberScheduler; 24 | template 25 | class EFiberLocal; 26 | 27 | abstract class EFiber: public EQueueEntry, 28 | public ERunnable, 29 | public enable_shared_from_this { 30 | public: 31 | enum State { 32 | NEW, 33 | RUNNABLE, 34 | BLOCKED, 35 | WAITING, 36 | TERMINATED 37 | }; 38 | 39 | static const int DEFAULT_STACK_SIZE = 1024*1024; //1M 40 | #ifdef __linux__ 41 | static const int MIN_STACK_SIZE = 8192; 42 | #endif 43 | #ifdef __APPLE__ 44 | static const int MIN_STACK_SIZE = 32768; 45 | #endif 46 | 47 | public: 48 | 49 | /** 50 | * The action to be performed by this fiber task. 51 | */ 52 | virtual void run() = 0; 53 | 54 | public: 55 | virtual ~EFiber(); 56 | 57 | /** 58 | * 59 | */ 60 | void setName(const char* name); 61 | 62 | /** 63 | * 64 | */ 65 | const char* getName(); 66 | 67 | /** 68 | * 69 | */ 70 | void setTag(long tag); 71 | 72 | /** 73 | * 74 | */ 75 | const long getTag(); 76 | 77 | /** 78 | * 79 | */ 80 | void cancel(); 81 | 82 | /** 83 | * 84 | */ 85 | boolean isCanceled(); 86 | 87 | /** 88 | * 89 | */ 90 | boolean isAlive(); 91 | 92 | /** 93 | * 94 | */ 95 | int getStackSize(); 96 | 97 | /** 98 | * 99 | */ 100 | int getId(); 101 | 102 | /** 103 | * 104 | */ 105 | int getThreadIndex(); 106 | 107 | /** 108 | * 109 | */ 110 | State getState(); 111 | 112 | /** 113 | * 114 | */ 115 | EFiberScheduler* getScheduler(); 116 | 117 | /** 118 | * 119 | */ 120 | EIoWaiter* getIoWaiter(); 121 | 122 | /** 123 | * 124 | */ 125 | boolean isWaitTimeout(); 126 | 127 | /** 128 | * 129 | */ 130 | virtual EString toString(); 131 | 132 | /** 133 | * 134 | */ 135 | static void yield(); 136 | 137 | /** 138 | * 139 | */ 140 | static void sleep(llong millis) THROWS(EInterruptedException); 141 | 142 | /** 143 | * 144 | */ 145 | static void sleep(llong millis, int nanos) THROWS(EInterruptedException); 146 | 147 | /** 148 | * 149 | */ 150 | static EFiber* currentFiber(); 151 | 152 | protected: 153 | friend class EContext; 154 | friend class EFiberScheduler; 155 | friend class EFiberBlocker; 156 | friend class EIoWaiter; 157 | template 158 | friend class EFiberLocal; 159 | template 160 | friend class EFiberConcurrentQueue; 161 | 162 | /* Fiber state */ 163 | volatile State state; 164 | 165 | /* Fiber ID */ 166 | int fid; // index from zero. 167 | /* Fiber name */ 168 | EString name; 169 | /* Fiber tag */ 170 | long tag; 171 | /* Fiber bound thread index*/ 172 | int threadIndex; // 0 is the EScheduler join()'s thread 173 | 174 | int stackSize; 175 | EContext* context; /* Fiber's context */ 176 | 177 | sp parent; /* Keep parent for sub fiber */ 178 | 179 | EFiberScheduler* scheduler; /* Bound scheduler */ 180 | EIoWaiter* iowaiter; /* Bound iowaiter */ 181 | 182 | EFiberConcurrentQueue* boundQueue; /* Thread bound queue */ 183 | long boundThreadID; 184 | 185 | EFiberBlocker* blocker; 186 | 187 | boolean isIoWaitTimeout; 188 | boolean canceled; 189 | 190 | es_hash_t* localValues; 191 | 192 | EFiberConcurrentQueue::NODE* packing; 193 | 194 | /** 195 | * Constructor 196 | */ 197 | EFiber(int stackSize = DEFAULT_STACK_SIZE); 198 | 199 | void setScheduler(EFiberScheduler* scheduler); 200 | void setIoWaiter(EIoWaiter* iowaiter); 201 | void setState(State state); 202 | void setThreadIndex(int index); 203 | 204 | void swapIn(); 205 | void swapOut(); 206 | }; 207 | 208 | #ifdef CPP11_SUPPORT 209 | class EFiberTarget: public EFiber { 210 | public: 211 | EFiberTarget(std::function f) { 212 | this->f = f; 213 | } 214 | virtual void run() { 215 | f(); 216 | } 217 | private: 218 | std::function f; 219 | }; 220 | #endif 221 | 222 | } /* namespace eco */ 223 | } /* namespace efc */ 224 | #endif /* EFIBER_HH_ */ 225 | -------------------------------------------------------------------------------- /src/EIoWaiter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EIoWaiter.cpp 3 | * 4 | * Created on: 2016-5-10 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "./EIoWaiter.hh" 9 | #include "../inc/EFiberDebugger.hh" 10 | 11 | #include 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | extern "C" { 17 | typedef ssize_t (*read_t)(int fd, void *buf, size_t count); 18 | extern read_t read_f; 19 | 20 | typedef ssize_t (*write_t)(int fd, const void *buf, size_t count); 21 | extern write_t write_f; 22 | } //!C 23 | 24 | static void pipeEventProc(co_poll_t *poll, int fd, void *clientData, int mask) { 25 | char buf[32]; 26 | int n; 27 | RESTARTABLE(read_f(fd, buf, sizeof(buf)), n); 28 | 29 | ECO_DEBUG(EFiberDebugger::WAITING, "io waiter signaled."); 30 | } 31 | 32 | //============================================================================= 33 | 34 | EIoWaiter::~EIoWaiter() { 35 | eco_poll_destroy(&poll); 36 | eso_pipe_destroy(&pipe); 37 | } 38 | 39 | EIoWaiter::EIoWaiter(int iosetSize): waiters(0) { 40 | poll = eco_poll_create(iosetSize); 41 | pipe = eso_pipe_create(); 42 | 43 | // register pipe for poll wakeup. 44 | eco_poll_file_event_update(poll, eso_fileno(pipe->in), ECO_POLL_READABLE, pipeEventProc, NULL); 45 | } 46 | 47 | void EIoWaiter::loopProcessEvents() { 48 | for (;;) { 49 | EFiber::yield(); //! 50 | 51 | int events = eco_poll_process_events(poll, ECO_POLL_ALL_EVENTS, 0); 52 | ECO_DEBUG(EFiberDebugger::WAITING, "get %d ready events", events); 53 | } 54 | } 55 | 56 | int EIoWaiter::onceProcessEvents(int timeout) { 57 | int events = eco_poll_process_events(poll, ECO_POLL_ALL_EVENTS, timeout); 58 | ECO_DEBUG(EFiberDebugger::WAITING, "get %d ready events, timeout=%d", events, timeout); 59 | return events; 60 | } 61 | 62 | void EIoWaiter::fileEventProc(co_poll_t *poll, int fd, void *clientData, int mask) { 63 | sp fiber = *(sp*)clientData; 64 | fiber->swapIn(); // resume! 65 | } 66 | 67 | void EIoWaiter::setFileEvent(int fd, int mask, sp fiber) { 68 | sp* f = new sp(fiber); 69 | es_status_t status = eco_poll_file_event_create(poll, fd, mask, fileEventProc, f); 70 | if (status != ES_SUCCESS) { 71 | delete f; 72 | } 73 | 74 | waiters++; 75 | 76 | fiber->state = EFiber::WAITING; // will be hang! 77 | ECO_DEBUG(EFiberDebugger::WAITING, "fiber[%s] will waiting.", fiber->toString().c_str()); 78 | } 79 | 80 | void EIoWaiter::delFileEvent(int fd, int mask) { 81 | coFileEvent *fe = NULL; 82 | if (fd >= poll->setsize) return; 83 | fe = &poll->events[fd]; 84 | if (fe) { 85 | sp* f = (sp*)fe->clientData; 86 | delete f; 87 | } 88 | eco_poll_file_event_delete(poll, fd, mask); 89 | 90 | waiters--; 91 | } 92 | 93 | int EIoWaiter::getFileEvent(int fd) { 94 | return eco_poll_get_file_events(poll, fd); 95 | } 96 | 97 | int EIoWaiter::timeEventProc(co_poll_t *poll, llong id, void *clientData) { 98 | sp fiber = *(sp*)clientData; 99 | fiber->isIoWaitTimeout = true; 100 | fiber->swapIn(); // resume! 101 | ECO_DEBUG(EFiberDebugger::WAITING, "fiber[%s] resume.", fiber->toString().c_str()); 102 | return ECO_POLL_NOMORE; 103 | } 104 | 105 | void EIoWaiter::timeEventFinalizerProc(co_poll_t *poll, void *clientData) { 106 | sp* f = (sp*)clientData; 107 | delete f; 108 | } 109 | 110 | llong EIoWaiter::setupTimer(llong timeout, sp fiber) { 111 | sp* f = new sp(fiber); 112 | fiber->isIoWaitTimeout = false; 113 | 114 | waiters++; 115 | 116 | return eco_poll_time_event_create(poll, timeout, timeEventProc, f, timeEventFinalizerProc); 117 | } 118 | 119 | void EIoWaiter::cancelTimer(llong id) { 120 | eco_poll_time_event_delete(poll, id); 121 | 122 | waiters--; 123 | } 124 | 125 | int EIoWaiter::getNativePollHandle() { 126 | return eco_poll_getfd(poll); 127 | } 128 | 129 | int EIoWaiter::getWaitersCount() { 130 | return waiters; 131 | } 132 | 133 | void EIoWaiter::interrupt() { 134 | eco_poll_time_fire_all(poll); 135 | } 136 | 137 | boolean EIoWaiter::swapOut(sp& fiber) { 138 | fiber->setState(EFiber::WAITING); 139 | EFiber::yield(); // paused! 140 | ECO_DEBUG(EFiberDebugger::WAITING, "fiber[%s] paused.", fiber->toString().c_str()); 141 | return true; 142 | } 143 | 144 | void EIoWaiter::signal() { 145 | int fd = eso_fileno(pipe->out); 146 | int n; 147 | RESTARTABLE(write_f(fd, "\0xF1", 1), n); 148 | } 149 | 150 | } /* namespace eco */ 151 | } /* namespace efc */ 152 | -------------------------------------------------------------------------------- /src/EFileContext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFileContext.cpp 3 | * 4 | * Created on: 2016-5-14 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "./EFileContext.hh" 9 | #include "./EHooker.hh" 10 | #include "../inc/EFiber.hh" 11 | 12 | #include 13 | #include 14 | 15 | namespace efc { 16 | namespace eco { 17 | 18 | extern "C" { 19 | typedef int (*fcntl_t)(int fd, int cmd, ...); 20 | extern fcntl_t fcntl_f; 21 | } //!C 22 | 23 | EFileContext::~EFileContext() { 24 | EFiber* fiber = EFiber::currentFiber(); 25 | if (!fiber) { 26 | int flags = fcntl_f(fd, F_GETFL); 27 | if ((userNonBlocked == true) && !(flags & O_NONBLOCK)) 28 | fcntl_f(fd, F_SETFL, flags | O_NONBLOCK); 29 | else if ((userNonBlocked == false) && (flags & O_NONBLOCK)) 30 | fcntl_f(fd, F_SETFL, flags & ~O_NONBLOCK); 31 | } 32 | } 33 | 34 | EFileContext::EFileContext(int fd): fd(fd), 35 | sysNonBlocked(false), 36 | userNonBlocked(false), 37 | recvTimeout(0), 38 | sendTimeout(0) { 39 | // set non blocking 40 | int flags = fcntl_f(fd, F_GETFL, 0); 41 | if (flags == -1) { //error 42 | throw EFileNotFoundException(__FILE__, __LINE__); 43 | } 44 | if ((flags & O_NONBLOCK)) { 45 | userNonBlocked = true; 46 | } else { 47 | fcntl_f(fd, F_SETFL, flags | O_NONBLOCK); 48 | sysNonBlocked = true; 49 | } 50 | 51 | if (isSocketFile(fd)) { 52 | // get SO_RCVTIMEO | SO_SNDTIMEO 53 | struct timeval tv; 54 | socklen_t len = sizeof(tv); 55 | int rv = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &len); 56 | if (rv == -1) { 57 | throw EFileNotFoundException(__FILE__, __LINE__); 58 | } 59 | recvTimeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; 60 | len = sizeof(tv); 61 | rv = getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &len); 62 | sendTimeout = tv.tv_sec * 1000 + tv.tv_usec / 1000; 63 | } 64 | } 65 | 66 | int EFileContext::configureNonBlocking() { 67 | int flags = fcntl_f(fd, F_GETFL); 68 | return fcntl_f(fd, F_SETFL, flags | O_NONBLOCK); 69 | } 70 | 71 | int EFileContext::inFD() { 72 | return fd; 73 | } 74 | 75 | void EFileContext::setSysNonBlock(boolean nonblock) { 76 | sysNonBlocked = nonblock; 77 | } 78 | 79 | boolean EFileContext::isSysNonBlocked() { 80 | return sysNonBlocked; 81 | } 82 | 83 | void EFileContext::setUserNonBlock(boolean nonblock) { 84 | userNonBlocked = nonblock; 85 | } 86 | 87 | boolean EFileContext::isUserNonBlocked() { 88 | return userNonBlocked; 89 | } 90 | 91 | void EFileContext::setRecvTimeout(const timeval* recvTimeout) { 92 | this->recvTimeout = recvTimeout->tv_sec * 1000 + recvTimeout->tv_usec / 1000; 93 | } 94 | 95 | int EFileContext::getRecvTimeout() { 96 | return recvTimeout; 97 | } 98 | 99 | void EFileContext::setSendTimeout(const timeval* sendTimeout) { 100 | this->sendTimeout = sendTimeout->tv_sec * 1000 + sendTimeout->tv_usec / 1000; 101 | } 102 | 103 | int EFileContext::getSendTimeout() { 104 | return sendTimeout; 105 | } 106 | 107 | boolean EFileContext::isStreamFile(int fd) { 108 | int mode; 109 | struct stat64 buf; 110 | 111 | if (::fstat64(fd, &buf) >= 0) { 112 | mode = buf.st_mode; 113 | if (S_ISFIFO(mode) || S_ISSOCK(mode)) { 114 | return true; 115 | } 116 | } 117 | 118 | return false; 119 | } 120 | 121 | boolean EFileContext::isSocketFile(int fd) { 122 | int mode; 123 | struct stat64 buf; 124 | 125 | if (::fstat64(fd, &buf) >= 0) { 126 | mode = buf.st_mode; 127 | if (S_ISSOCK(mode)) { 128 | return true; 129 | } 130 | } 131 | 132 | return false; 133 | } 134 | 135 | //============================================================================= 136 | 137 | EFileContextManager::EFileContextManager(int maxfd) : 138 | hookedFiles((ES_ALIGN_UP(maxfd, FD_CHUNK_CAPACITY) / FD_CHUNK_CAPACITY), 139 | std::vector < sp > (FD_CHUNK_CAPACITY)) { 140 | // 141 | } 142 | 143 | EFileContextManager::~EFileContextManager() { 144 | // 145 | } 146 | 147 | sp EFileContextManager::get(int fd) { 148 | SpinLock* lock = LOCKFOR((void*)fd); 149 | int index0 = ES_ALIGN_UP(fd, FD_CHUNK_CAPACITY) / FD_CHUNK_CAPACITY - 1; 150 | sp fc; 151 | lock->lock(); 152 | int index1 = fd % FD_CHUNK_CAPACITY - 1; 153 | fc = hookedFiles[index0][index1]; 154 | if (fc == null) { 155 | fc = hookedFiles[index0][index1] = new EFileContext(fd); 156 | } 157 | lock->unlock(); 158 | return fc; 159 | } 160 | 161 | void EFileContextManager::remove(int fd) { 162 | SpinLock* lock = LOCKFOR((void*)fd); 163 | int index0 = ES_ALIGN_UP(fd, FD_CHUNK_CAPACITY) / FD_CHUNK_CAPACITY - 1; 164 | int index1 = fd % FD_CHUNK_CAPACITY - 1; 165 | lock->lock(); 166 | hookedFiles[index0][index1].reset(); 167 | lock->unlock(); 168 | } 169 | 170 | void EFileContextManager::clear() { 171 | hookedFiles.clear(); 172 | } 173 | 174 | } /* namespace eco */ 175 | } /* namespace efc */ 176 | -------------------------------------------------------------------------------- /src/EFiberBlocker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberBlocker.cpp 3 | * 4 | * Created on: 2016-5-9 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "./EIoWaiter.hh" 9 | #include "../inc/EFiberBlocker.hh" 10 | #include "../inc/EFiberScheduler.hh" 11 | #include "../inc/EFiberDebugger.hh" 12 | 13 | namespace efc { 14 | namespace eco { 15 | 16 | EFiberBlocker::~EFiberBlocker() { 17 | #ifdef DEBUG 18 | if (!sync_->getLock()->tryLock()) { 19 | ECO_DEBUG(EFiberDebugger::BLOCKER, "fiber blocker#%p is locked.", this); 20 | } 21 | 22 | if (!waitQueue.size() > 0) { 23 | ECO_DEBUG(EFiberDebugger::BLOCKER, "fiber blocker#%p is waiting.", this); 24 | } 25 | #endif 26 | } 27 | 28 | EFiberBlocker::EFiberBlocker(uint capacity, uint limit): 29 | wakeup_(capacity), 30 | limit_(limit), 31 | sync_(new SyncObj) { 32 | ECO_DEBUG(EFiberDebugger::BLOCKER, "fiber blocker#%p created.", this); 33 | } 34 | 35 | void EFiberBlocker::wait() { 36 | EFiber* fiber = EFiber::currentFiber(); 37 | if (fiber) { 38 | // waiter is a fiber. 39 | SYNCHRONIZED(sync_.get()) { 40 | if (wakeup_ > 0) { 41 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wait immediately done."); 42 | wakeup_--; 43 | return ; 44 | } 45 | }} 46 | 47 | fiber->blocker = this; 48 | fiber->state = EFiber::BLOCKED; // will be hang! 49 | EFiber::yield(); 50 | } else { 51 | // waiter is a thread. 52 | SYNCHRONIZED(sync_.get()) { 53 | if (wakeup_ > 0) { 54 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wait immediately done."); 55 | wakeup_--; 56 | return ; 57 | } 58 | 59 | waitQueue.add(sync_); 60 | ECO_DEBUG(EFiberDebugger::BLOCKER, "thread[%s] will waiting.", EThread::currentThread()->toString().c_str()); 61 | sync_->wait(); 62 | }} 63 | } 64 | } 65 | 66 | boolean EFiberBlocker::tryWait() { 67 | SYNCHRONIZED(sync_.get()) { 68 | if (wakeup_ == 0) 69 | return false; 70 | 71 | wakeup_--; 72 | ECO_DEBUG(EFiberDebugger::BLOCKER, "try wait success."); 73 | return true; 74 | }} 75 | } 76 | 77 | boolean EFiberBlocker::tryWait(llong time, ETimeUnit* unit) { 78 | EFiber* fiber = EFiber::currentFiber(); 79 | if (fiber) { 80 | // waiter is a fiber. 81 | SYNCHRONIZED(sync_.get()) { 82 | if (wakeup_ > 0) { 83 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wait immediately done."); 84 | wakeup_--; 85 | return true; 86 | } 87 | }} 88 | 89 | // add timeout check 90 | EIoWaiter* ioWaiter = EFiberScheduler::currentIoWaiter(); 91 | 92 | llong timeout = unit->toMillis(time); 93 | llong timerID = -1; 94 | if (timeout > 0) { 95 | timerID = ioWaiter->setupTimer(timeout, fiber->shared_from_this()); 96 | } 97 | 98 | fiber->blocker = this; 99 | fiber->state = EFiber::BLOCKED; // will be hang! 100 | EFiber::yield(); 101 | 102 | if (timerID != -1) { 103 | ioWaiter->cancelTimer(timerID); 104 | } 105 | 106 | if (fiber->isWaitTimeout()) { 107 | return !waitQueue.remove(fiber); 108 | } 109 | return true; 110 | } else { 111 | // waiter is a thread. 112 | SYNCHRONIZED(sync_.get()) { 113 | if (wakeup_ > 0) { 114 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wait immediately done."); 115 | wakeup_--; 116 | return true; 117 | } 118 | 119 | waitQueue.add(sync_); 120 | ECO_DEBUG(EFiberDebugger::BLOCKER, "thread[%s] will waiting.", EThread::currentThread()->toString().c_str()); 121 | boolean r = sync_->getCondition()->await(time, unit); 122 | if (!r) { 123 | return !waitQueue.remove(sync_.get()); 124 | } 125 | return true; 126 | }} 127 | } 128 | } 129 | 130 | boolean EFiberBlocker::wakeUp() { 131 | sp waiter; 132 | 133 | SYNCHRONIZED(sync_.get()) { 134 | waiter = waitQueue.poll(); 135 | if (!waiter) { 136 | if (wakeup_ >= limit_) { 137 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wake up failed."); 138 | return false; 139 | } 140 | wakeup_++; 141 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wake up #%u.", wakeup_); 142 | return true; 143 | } 144 | 145 | sp sync = dynamic_pointer_cast(waiter); 146 | if (sync != null) { // waiter is a thread. 147 | sync->notify(); 148 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wake up a thread waiter."); 149 | return true; 150 | } 151 | }} 152 | 153 | // if waiter is not a thread that's the fiber. 154 | sp fiber = dynamic_pointer_cast(waiter); 155 | ECO_DEBUG(EFiberDebugger::BLOCKER, "wake up a fiber[%s].", fiber->toString().c_str()); 156 | fiber->swapIn(); // resume! 157 | return true; 158 | } 159 | 160 | boolean EFiberBlocker::isWaking() { 161 | SYNCHRONIZED(sync_.get()) { 162 | return wakeup_ > 0; 163 | }} 164 | } 165 | 166 | boolean EFiberBlocker::swapOut(EFiber* fiber) { 167 | SYNCHRONIZED(sync_.get()) { 168 | if (wakeup_ > 0) { 169 | wakeup_--; 170 | return false; 171 | } 172 | ECO_DEBUG(EFiberDebugger::BLOCKER, "fiber[%s] will waiting.", fiber->toString().c_str()); 173 | waitQueue.add(fiber->shared_from_this()); 174 | return true; // hung! 175 | }} 176 | } 177 | 178 | } /* namespace eco */ 179 | } /* namespace efc */ 180 | -------------------------------------------------------------------------------- /src/EFiber.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiber.cpp 3 | * 4 | * Created on: 2016-5-17 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "./EContext.hh" 9 | #include "./EIoWaiter.hh" 10 | #include "../inc/EFiber.hh" 11 | #include "../inc/EFiberLocal.hh" 12 | #include "../inc/EFiberScheduler.hh" 13 | #include "../inc/EFiberBlocker.hh" 14 | #include "../inc/EFiberDebugger.hh" 15 | 16 | namespace efc { 17 | namespace eco { 18 | 19 | static EAtomicCounter idCounter(0); 20 | 21 | static const char StateName[][15] = { 22 | "NEW", 23 | "RUNNABLE", 24 | "BLOCKED", 25 | "WAITING", 26 | "TERMINATED" 27 | }; 28 | 29 | EFiber::~EFiber() { 30 | delete packing; 31 | delete context; 32 | /* 33 | * call back for user has a chance to free all fiber local data. 34 | */ 35 | es_hash_index_t *hi; 36 | void *key; 37 | void *val; 38 | for (hi = eso_hash_first(localValues); hi; hi = eso_hash_next(hi)) { 39 | eso_hash_this(hi, &key, NULL, &val); 40 | EFiberLocalKeyWrap* kw = (EFiberLocalKeyWrap*)key; 41 | if (kw) { 42 | kw->callback(val); 43 | } 44 | } 45 | eso_hash_free(&localValues); 46 | ECO_DEBUG(EFiberDebugger::FIBER, "delete fiber#%d", fid); 47 | } 48 | 49 | EFiber::EFiber(int size): 50 | state(NEW), 51 | fid(idCounter++), 52 | tag(ES_LONG_MIN_VALUE), 53 | stackSize(ES_MAX(size, MIN_STACK_SIZE)), 54 | context(null), 55 | scheduler(null), 56 | iowaiter(null), 57 | boundQueue(null), 58 | boundThreadID(-1), 59 | blocker(null), 60 | isIoWaitTimeout(false), 61 | canceled(false), 62 | packing(null), 63 | threadIndex(0) { 64 | EFiber* cf = currentFiber(); 65 | if (cf) parent = cf->shared_from_this(); 66 | context = new EContext(this); 67 | localValues = eso_hash_make(1, NULL); 68 | packing = new EFiberConcurrentQueue::NODE(); 69 | ECO_DEBUG(EFiberDebugger::FIBER, "new fiber#%d: %d", fid, stackSize); 70 | } 71 | 72 | void EFiber::setName(const char* name) { 73 | this->name = name; 74 | } 75 | 76 | const char* EFiber::getName() { 77 | return name.isEmpty() ? "null" : name.c_str(); 78 | } 79 | 80 | void EFiber::setTag(long tag) { 81 | this->tag = tag; 82 | } 83 | 84 | const long EFiber::getTag() { 85 | return tag; 86 | } 87 | 88 | void EFiber::cancel() { 89 | canceled = true; 90 | } 91 | 92 | boolean EFiber::isCanceled() { 93 | return canceled; 94 | } 95 | 96 | boolean EFiber::isAlive() { 97 | return (state == RUNNABLE); 98 | } 99 | 100 | int EFiber::getStackSize() { 101 | return stackSize; 102 | } 103 | 104 | int EFiber::getId() { 105 | return fid; 106 | } 107 | 108 | int EFiber::getThreadIndex() { 109 | return threadIndex; 110 | } 111 | 112 | EFiber::State EFiber::getState() { 113 | return state; 114 | } 115 | 116 | EFiberScheduler* EFiber::getScheduler() { 117 | return scheduler; 118 | } 119 | 120 | EIoWaiter* EFiber::getIoWaiter() { 121 | return iowaiter; 122 | } 123 | 124 | boolean EFiber::isWaitTimeout() { 125 | boolean b = isIoWaitTimeout; 126 | isIoWaitTimeout = false; 127 | return b; 128 | } 129 | 130 | void EFiber::yield() { 131 | EFiber* f = EFiber::currentFiber(); 132 | if (f != null) { 133 | f->context->swapOut(); 134 | } 135 | } 136 | 137 | EString EFiber::toString() { 138 | return EString::formatOf("Fiber[%s,%d,%s,%d]", getName(), getId(), StateName[state], stackSize); 139 | } 140 | 141 | void EFiber::sleep(llong millis) { 142 | if (millis < 0) { 143 | throw EIllegalArgumentException(__FILE__, __LINE__, "timeout value is negative"); 144 | } 145 | 146 | EIoWaiter* ioWaiter = EFiberScheduler::currentIoWaiter(); 147 | if (ioWaiter) { 148 | sp self = EFiber::currentFiber()->shared_from_this(); 149 | llong id = ioWaiter->setupTimer(millis, self); 150 | ioWaiter->swapOut(self); 151 | ioWaiter->cancelTimer(id); 152 | } 153 | 154 | EFiberScheduler* scheduler = EFiberScheduler::currentScheduler(); 155 | if (scheduler && scheduler->isInterrupted()) { 156 | throw EInterruptedException(__FILE__, __LINE__, "Interrupted."); 157 | } 158 | } 159 | 160 | void EFiber::sleep(llong millis, int nanos) { 161 | if (millis < 0) { 162 | throw EIllegalArgumentException(__FILE__, __LINE__, "timeout value is negative"); 163 | } 164 | 165 | if (nanos < 0 || nanos > 999999) { 166 | throw EIllegalArgumentException(__FILE__, __LINE__, 167 | "nanosecond timeout value out of range"); 168 | } 169 | 170 | if (nanos >= 500000 || (nanos != 0 && millis == 0)) { 171 | millis++; 172 | } 173 | 174 | sleep(millis); 175 | } 176 | 177 | EFiber* EFiber::currentFiber() { 178 | return EFiberScheduler::activeFiber(); 179 | } 180 | 181 | void EFiber::setScheduler(EFiberScheduler* scheduler) { 182 | this->scheduler = scheduler; 183 | } 184 | 185 | void EFiber::setIoWaiter(EIoWaiter* iowaiter) { 186 | this->iowaiter = iowaiter; 187 | } 188 | 189 | void EFiber::setState(State state) { 190 | this->state = state; 191 | } 192 | 193 | void EFiber::setThreadIndex(int index) { 194 | this->threadIndex = index; 195 | } 196 | 197 | void EFiber::swapIn() { 198 | int _state_ = state; 199 | if (state == EFiber::WAITING || state == EFiber::BLOCKED) { 200 | blocker = null; 201 | state = EFiber::RUNNABLE; 202 | boundQueue->add(this->packing->value); 203 | 204 | if (EThread::currentThread()->getId() != boundThreadID 205 | && _state_ == EFiber::BLOCKED) { 206 | iowaiter->signal(); 207 | } 208 | } 209 | } 210 | 211 | void EFiber::swapOut() { 212 | context->swapOut(); 213 | } 214 | 215 | } /* namespace eco */ 216 | } /* namespace efc */ 217 | -------------------------------------------------------------------------------- /src/mach_hook.c: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | 3 | #include "mach_hook.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef SEG_DATA_CONST 11 | #define SEG_DATA_CONST "__DATA_CONST" 12 | #endif 13 | 14 | struct rebindings_entry { 15 | struct rebinding *rebindings; 16 | size_t rebindings_nel; 17 | struct rebindings_entry *next; 18 | }; 19 | 20 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 21 | struct rebinding rebindings[], size_t nel) { 22 | struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc( 23 | sizeof(struct rebindings_entry)); 24 | if (!new_entry) { 25 | return -1; 26 | } 27 | new_entry->rebindings = (struct rebinding *) malloc( 28 | sizeof(struct rebinding) * nel); 29 | if (!new_entry->rebindings) { 30 | free(new_entry); 31 | return -1; 32 | } 33 | memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); 34 | new_entry->rebindings_nel = nel; 35 | new_entry->next = *rebindings_head; 36 | *rebindings_head = new_entry; 37 | return 0; 38 | } 39 | 40 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 41 | section_t *section, intptr_t slide, nlist_t *symtab, char *strtab, 42 | uint32_t *indirect_symtab) { 43 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 44 | void **indirect_symbol_bindings = (void **) ((uintptr_t) slide 45 | + section->addr); 46 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 47 | uint32_t symtab_index = indirect_symbol_indices[i]; 48 | if (symtab_index == INDIRECT_SYMBOL_ABS 49 | || symtab_index == INDIRECT_SYMBOL_LOCAL 50 | || symtab_index 51 | == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 52 | continue; 53 | } 54 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 55 | char *symbol_name = strtab + strtab_offset; 56 | bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; 57 | struct rebindings_entry *cur = rebindings; 58 | while (cur) { 59 | for (uint j = 0; j < cur->rebindings_nel; j++) { 60 | if (symbol_name_longer_than_1 61 | && strcmp(&symbol_name[1], cur->rebindings[j].name) 62 | == 0) { 63 | if (cur->rebindings[j].replaced != NULL 64 | && indirect_symbol_bindings[i] 65 | != cur->rebindings[j].replacement) { 66 | *(cur->rebindings[j].replaced) = 67 | indirect_symbol_bindings[i]; 68 | } 69 | indirect_symbol_bindings[i] = 70 | cur->rebindings[j].replacement; 71 | goto symbol_loop; 72 | } 73 | } 74 | cur = cur->next; 75 | } 76 | symbol_loop: ; 77 | } 78 | } 79 | 80 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 81 | const struct mach_header *header, intptr_t slide) { 82 | Dl_info info; 83 | if (dladdr(header, &info) == 0) { 84 | return; 85 | } 86 | 87 | segment_command_t *cur_seg_cmd; 88 | segment_command_t *linkedit_segment = NULL; 89 | struct symtab_command* symtab_cmd = NULL; 90 | struct dysymtab_command* dysymtab_cmd = NULL; 91 | 92 | uintptr_t cur = (uintptr_t) header + sizeof(mach_header_t); 93 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 94 | cur_seg_cmd = (segment_command_t *) cur; 95 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 96 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 97 | linkedit_segment = cur_seg_cmd; 98 | } 99 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 100 | symtab_cmd = (struct symtab_command*) cur_seg_cmd; 101 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 102 | dysymtab_cmd = (struct dysymtab_command*) cur_seg_cmd; 103 | } 104 | } 105 | 106 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment 107 | || !dysymtab_cmd->nindirectsyms) { 108 | return; 109 | } 110 | 111 | // Find base symbol/string table addresses 112 | uintptr_t linkedit_base = (uintptr_t) slide + linkedit_segment->vmaddr 113 | - linkedit_segment->fileoff; 114 | nlist_t *symtab = (nlist_t *) (linkedit_base + symtab_cmd->symoff); 115 | char *strtab = (char *) (linkedit_base + symtab_cmd->stroff); 116 | 117 | // Get indirect symbol table (array of uint32_t indices into symbol table) 118 | uint32_t *indirect_symtab = (uint32_t *) (linkedit_base 119 | + dysymtab_cmd->indirectsymoff); 120 | 121 | cur = (uintptr_t) header + sizeof(mach_header_t); 122 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 123 | cur_seg_cmd = (segment_command_t *) cur; 124 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 125 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 126 | && strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 127 | continue; 128 | } 129 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 130 | section_t *sect = 131 | (section_t *) (cur + sizeof(segment_command_t)) + j; 132 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 133 | perform_rebinding_with_section(rebindings, sect, slide, 134 | symtab, strtab, indirect_symtab); 135 | } 136 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 137 | perform_rebinding_with_section(rebindings, sect, slide, 138 | symtab, strtab, indirect_symtab); 139 | } 140 | } 141 | } 142 | } 143 | } 144 | 145 | int rebind_symbols_image(void *header, intptr_t slide, 146 | struct rebinding rebindings[], size_t rebindings_nel) { 147 | struct rebindings_entry *rebindings_head = NULL; 148 | int retval = prepend_rebindings(&rebindings_head, rebindings, 149 | rebindings_nel); 150 | rebind_symbols_for_image(rebindings_head, 151 | (const struct mach_header *) header, slide); 152 | if (rebindings_head) { 153 | free(rebindings_head->rebindings); 154 | } 155 | free(rebindings_head); 156 | return retval; 157 | } 158 | 159 | #endif //!__APPLE__ 160 | -------------------------------------------------------------------------------- /inc/EFiberScheduler.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberScheduler.hh 3 | * 4 | * Created on: 2016-5-17 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #ifndef EFIBERSCHEDULER_HH_ 9 | #define EFIBERSCHEDULER_HH_ 10 | 11 | #include "./EFiber.hh" 12 | #include "./EFiberTimer.hh" 13 | #include "./EFiberUtil.hh" 14 | 15 | #ifdef CPP11_SUPPORT 16 | #include 17 | #endif 18 | 19 | 20 | namespace efc { 21 | namespace eco { 22 | 23 | /** 24 | * Fiber scheduler: only for signal-threading. 25 | */ 26 | 27 | class EFiber; 28 | class EIoWaiter; 29 | class EFileContext; 30 | class EFileContextManager; 31 | class SchedulerStub; 32 | 33 | class EFiberScheduler: public EObject { 34 | public: 35 | /** 36 | * Schedule phase for per-thread. 37 | */ 38 | enum SchedulePhase { 39 | SCHEDULE_BEFORE = 0, 40 | SCHEDULE_IDLE = 1, 41 | FIBER_BEFORE = 2, 42 | FIBER_AFTER = 3, 43 | SCHEDULE_AFTER = 4 44 | }; 45 | 46 | /** 47 | * Type of the callback for per-thread schedule. 48 | */ 49 | typedef void thread_schedule_callback_t(int threadIndex, 50 | SchedulePhase schedulePhase, EThread* currentThread, 51 | EFiber* currentFiber); 52 | 53 | /** 54 | * Type of fiber schedule balancer 55 | * 56 | * @return SchedulerStub id (threadIndex), 0 always the call join()'s thread. 57 | */ 58 | typedef int fiber_schedule_balance_t(EFiber* fiber, int threadNums); 59 | 60 | public: 61 | virtual ~EFiberScheduler(); 62 | 63 | /** 64 | * New scheduler instance for signal-thread. 65 | */ 66 | EFiberScheduler(); 67 | EFiberScheduler(int maxfd); 68 | 69 | /** 70 | * Add a new fiber to this scheduler 71 | */ 72 | virtual void schedule(sp fiber); 73 | 74 | /** 75 | * Add a new fiber to this scheduler 76 | * and locked in parent fiber's thread. 77 | */ 78 | virtual void scheduleInheritThread(sp fiber); 79 | 80 | #ifdef CPP11_SUPPORT 81 | /** 82 | * Add a new lambda function as fiber to this scheduler (c++11) 83 | */ 84 | virtual sp schedule(std::function f, int stackSize=1024*1024); 85 | 86 | /** 87 | * Add a new lambda function as fiber to this scheduler (c++11) 88 | * and locked in parent fiber's thread. 89 | */ 90 | virtual sp scheduleInheritThread(std::function f, int stackSize=1024*1024); 91 | #endif 92 | 93 | /** 94 | * Add a new timer to this scheduler 95 | */ 96 | virtual sp addtimer(sp timer, llong delay); 97 | virtual sp addtimer(sp timer, EDate* time); 98 | virtual sp addtimer(sp timer, llong delay, llong period); 99 | virtual sp addtimer(sp timer, EDate* firstTime, llong period); 100 | 101 | #ifdef CPP11_SUPPORT 102 | /** 103 | * Add a lambda function as timer to this scheduler (c++11) 104 | */ 105 | virtual void addtimer(std::function f, llong delay); 106 | virtual void addtimer(std::function f, EDate* time); 107 | virtual void addtimer(std::function f, llong delay, llong period); 108 | virtual void addtimer(std::function f, EDate* firstTime, llong period); 109 | #endif 110 | 111 | /** 112 | * Set the callback for scheduler looping 113 | * 114 | * @param callback Schedule callback at different phase. 115 | */ 116 | #ifdef CPP11_SUPPORT 117 | virtual void setScheduleCallback(std::function callback); 120 | #else 121 | virtual void setScheduleCallback(thread_schedule_callback_t* callback); 122 | #endif 123 | 124 | /** 125 | * Set the callback for schedule balance 126 | * 127 | * @param balancer if null then balance use rol-poling else use the callback return value 128 | */ 129 | #ifdef CPP11_SUPPORT 130 | virtual void setBalanceCallback(std::function balancer); 131 | #else 132 | virtual void setBalanceCallback(fiber_schedule_balance_t* balancer); 133 | #endif 134 | 135 | /** 136 | * Do schedule and wait all fibers work done. 137 | */ 138 | virtual void join(); 139 | 140 | /** 141 | * Do schedule with thread pool and wait all fibers work done. 142 | * 143 | * @param threadNums >= 1 144 | */ 145 | virtual void join(int threadNums); 146 | 147 | /** 148 | * 149 | */ 150 | virtual void interrupt(); 151 | virtual boolean isInterrupted(); 152 | 153 | /** 154 | * 155 | */ 156 | virtual int totalFiberCount(); 157 | 158 | /** 159 | * Get current active fiber. 160 | */ 161 | static EFiber* activeFiber(); 162 | 163 | /** 164 | * Get current joined scheduler. 165 | */ 166 | static EFiberScheduler* currentScheduler(); 167 | static EIoWaiter* currentIoWaiter(); 168 | 169 | public: 170 | sp getFileContext(int fd); 171 | void delFileContext(int fd); 172 | void clearFileContexts(); 173 | 174 | private: 175 | friend class EFiber; 176 | 177 | int maxEventSetSize; 178 | int threadNums; 179 | 180 | EFiberConcurrentQueue defaultTaskQueue; 181 | EA* schedulerStubs; // created only if threadNums > 1 182 | #ifdef CPP11_SUPPORT 183 | std::function balanceCallback; 184 | #else 185 | fiber_schedule_balance_t* balanceCallback; 186 | #endif 187 | EAtomicCounter balanceIndex; 188 | 189 | EAtomicBoolean hasError; 190 | EAtomicCounter totalFiberCounter; 191 | 192 | EFileContextManager* hookedFiles; 193 | 194 | volatile boolean interrupted; 195 | 196 | #ifdef CPP11_SUPPORT 197 | std::function scheduleCallback; 200 | #else 201 | thread_schedule_callback_t* scheduleCallback; 202 | #endif 203 | 204 | static EThreadLocalStorage currScheduler; 205 | static EThreadLocalStorage currIoWaiter; 206 | 207 | /** 208 | * 209 | */ 210 | void joinWithThreadBind(EA* schedulerStubs, int index, 211 | EThread* currentThread); 212 | 213 | void scheduleIgnoreBalance(sp fiber, boolean ignoreBalance); 214 | }; 215 | 216 | } /* namespace eco */ 217 | } /* namespace efc */ 218 | #endif /* EFIBERSCHEDULER_HH_ */ 219 | -------------------------------------------------------------------------------- /src/EContext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EContext.cpp 3 | * 4 | * Created on: 2016-5-1 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "./EContext.hh" 9 | #include "../inc/EFiber.hh" 10 | #include "../inc/EFiberDebugger.hh" 11 | 12 | namespace efc { 13 | namespace eco { 14 | 15 | #if defined(__x86_64__) 16 | # if defined(__AVX__) 17 | # define CLOBBER \ 18 | , "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",\ 19 | "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" 20 | # else 21 | # define CLOBBER 22 | # endif 23 | 24 | # define SETJMP(ctx) ({\ 25 | int ret;\ 26 | asm("lea LJMPRET%=(%%rip), %%rcx\n\t"\ 27 | "xor %%rax, %%rax\n\t"\ 28 | "mov %%rbx, (%%rdx)\n\t"\ 29 | "mov %%rbp, 8(%%rdx)\n\t"\ 30 | "mov %%r12, 16(%%rdx)\n\t"\ 31 | "mov %%rsp, 24(%%rdx)\n\t"\ 32 | "mov %%r13, 32(%%rdx)\n\t"\ 33 | "mov %%r14, 40(%%rdx)\n\t"\ 34 | "mov %%r15, 48(%%rdx)\n\t"\ 35 | "mov %%rcx, 56(%%rdx)\n\t"\ 36 | "mov %%rdi, 64(%%rdx)\n\t"\ 37 | "mov %%rsi, 72(%%rdx)\n\t"\ 38 | "LJMPRET%=:\n\t"\ 39 | : "=a" (ret)\ 40 | : "d" (ctx)\ 41 | : "memory", "rcx", "r8", "r9", "r10", "r11",\ 42 | "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",\ 43 | "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"\ 44 | CLOBBER\ 45 | );\ 46 | ret;\ 47 | }) 48 | 49 | # define LONGJMP(ctx) \ 50 | asm("movq (%%rax), %%rbx\n\t"\ 51 | "movq 8(%%rax), %%rbp\n\t"\ 52 | "movq 16(%%rax), %%r12\n\t"\ 53 | "movq 24(%%rax), %%rdx\n\t"\ 54 | "movq 32(%%rax), %%r13\n\t"\ 55 | "movq 40(%%rax), %%r14\n\t"\ 56 | "mov %%rdx, %%rsp\n\t"\ 57 | "movq 48(%%rax), %%r15\n\t"\ 58 | "movq 56(%%rax), %%rdx\n\t"\ 59 | "movq 64(%%rax), %%rdi\n\t"\ 60 | "movq 72(%%rax), %%rsi\n\t"\ 61 | "jmp *%%rdx\n\t"\ 62 | : : "a" (ctx) : "rdx" \ 63 | ) 64 | #endif 65 | 66 | //============================================================================= 67 | 68 | EThreadLocalStorage EContext::threadLocal; 69 | 70 | void* EContext::getOrignContext() { 71 | #ifdef __x86_64__ 72 | void* env = threadLocal.get(); 73 | if (!env) { 74 | env = malloc(sizeof(es_uint64_t) * 10); 75 | threadLocal.set(env); 76 | } 77 | return env; 78 | #else 79 | ucontext_t* ctx = (ucontext_t*)threadLocal.get(); 80 | if (!ctx) { 81 | ctx = new ucontext_t; 82 | threadLocal.set(ctx); 83 | } 84 | return ctx; 85 | #endif 86 | } 87 | 88 | void EContext::cleanOrignContext() { 89 | #ifdef __x86_64__ 90 | void* env = threadLocal.get(); 91 | if (env) { 92 | free(env); 93 | threadLocal.set(NULL); 94 | } 95 | #else 96 | ucontext_t* ctx = (ucontext_t*)threadLocal.get(); 97 | if (ctx) { 98 | delete ctx; 99 | threadLocal.set(NULL); 100 | } 101 | #endif 102 | } 103 | 104 | //============================================================================= 105 | 106 | EContext::~EContext() { 107 | if (stackAddr) { 108 | free(stackAddr); 109 | } 110 | if (context) { 111 | free(context); 112 | } 113 | } 114 | 115 | EContext::EContext(EFiber* f): fiber(f) { 116 | context = (ucontext_t*)malloc(sizeof(ucontext_t)); 117 | 118 | /* do a reasonable initialization */ 119 | sigset_t zero; 120 | memset(context, 0, sizeof(ucontext_t)); 121 | sigemptyset(&zero); 122 | sigprocmask(SIG_BLOCK, &zero, &context->uc_sigmask); 123 | 124 | /* must initialize with current context */ 125 | if (getcontext(context) == -1) { 126 | throw ERuntimeException(__FILE__, __LINE__, "getcontext"); 127 | } 128 | 129 | int stackSize = fiber->stackSize; 130 | #ifdef DEBUG 131 | stackAddr = (char*)calloc(1, stackSize); //for calc max stack. 132 | //@see: http://embeddedgurus.com/stack-overflow/2009/03/computing-your-stack-size/ 133 | #else 134 | stackAddr = (char*)malloc(stackSize); 135 | #endif 136 | 137 | /* call makecontext to do the real work. */ 138 | /* leave a few words open on both ends */ 139 | char* alignedAddr = (char*)ES_ALIGN_DEFAULT((llong)stackAddr); 140 | context->uc_stack.ss_sp = alignedAddr; //memory aligned 141 | context->uc_stack.ss_size = stackSize - (alignedAddr - stackAddr); 142 | context->uc_link = NULL; 143 | 144 | makecontext(context, (void(*)(void))&fiber_worker, 1, fiber); 145 | } 146 | 147 | boolean EContext::swapIn() { 148 | // restore 149 | errno = errno_; 150 | 151 | #ifdef __x86_64__ 152 | /* use setcontext() for the initial jump, as it allows us to set up 153 | * a stack, but continue with longjmp() as it's much faster. 154 | */ 155 | if (SETJMP(getOrignContext()) == 0) { 156 | /* context just be used once for set up a stack, which will 157 | * be freed in fiber_worker. 158 | */ 159 | if (context != NULL) 160 | setcontext(context); 161 | else 162 | LONGJMP(env); 163 | } 164 | return true; 165 | #else 166 | return (swapcontext((ucontext_t*)getOrignContext(), context) == 0); 167 | #endif 168 | } 169 | 170 | boolean EContext::swapOut() { 171 | #ifdef DEBUG 172 | //calc max stack size!!! 173 | if (EFiberDebugger::getInstance().isDebugOn(EFiberDebugger::FIBER)) { 174 | char* pcurr = stackAddr; 175 | char* pend = stackAddr + fiber->stackSize; 176 | while (pcurr < pend && *pcurr++ == 0) { 177 | } 178 | int n = pend - pcurr; 179 | ECO_DEBUG(EFiberDebugger::FIBER, "======= max stack size: %d =======", n); 180 | } 181 | #endif 182 | 183 | //keep it 184 | errno_ = errno; 185 | 186 | #ifdef __x86_64__ 187 | if (SETJMP(env) == 0) { 188 | LONGJMP(getOrignContext()); 189 | } 190 | return true; 191 | #else 192 | return (swapcontext(context, (ucontext_t*)getOrignContext()) == 0); 193 | #endif 194 | } 195 | 196 | void EContext::fiber_worker(void* arg) { 197 | EFiber* fiber = (EFiber*)arg; 198 | 199 | #ifdef __x86_64__ 200 | /* when using setjmp/longjmp, the context just be used only once */ 201 | if (fiber->context->context != NULL) { 202 | free(fiber->context->context); 203 | fiber->context->context = NULL; 204 | } 205 | #endif 206 | 207 | try { 208 | 209 | fiber->run(); 210 | 211 | } catch (EThrowable& t) { 212 | t.printStackTrace(); 213 | throw; 214 | } catch (...) { 215 | //TODO... 216 | throw; 217 | } 218 | 219 | fiber->state = EFiber::TERMINATED; 220 | fiber->context->swapOut(); //EFiber::yield(); 221 | } 222 | 223 | } /* namespace eco */ 224 | } /* namespace efc */ 225 | -------------------------------------------------------------------------------- /src/eco_ae_kqueue.c: -------------------------------------------------------------------------------- 1 | /* Kqueue(2)-based ae.c module 2 | * 3 | * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifdef HAVE_KQUEUE 32 | 33 | #include 34 | 35 | typedef int (*kevent_t)(int kq, const struct kevent *changelist, int nchanges, 36 | struct kevent *eventlist, int nevents, const struct timespec *timeout); 37 | extern kevent_t kevent_f; 38 | 39 | typedef struct aeApiState { 40 | int kqfd; 41 | struct kevent *events; 42 | } aeApiState; 43 | 44 | static int aeApiCreate(aeEventLoop *eventLoop) { 45 | aeApiState *state = malloc(sizeof(aeApiState)); 46 | 47 | if (!state) return -1; 48 | state->events = malloc(sizeof(struct kevent)*eventLoop->setsize); 49 | if (!state->events) { 50 | free(state); 51 | return -1; 52 | } 53 | state->kqfd = kqueue(); 54 | if (state->kqfd == -1) { 55 | free(state->events); 56 | free(state); 57 | return -1; 58 | } 59 | eventLoop->apidata = state; 60 | 61 | return 0; 62 | } 63 | 64 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 65 | aeApiState *state = eventLoop->apidata; 66 | 67 | state->events = realloc(state->events, sizeof(struct kevent)*setsize); 68 | return 0; 69 | } 70 | 71 | static void aeApiFree(aeEventLoop *eventLoop) { 72 | aeApiState *state = eventLoop->apidata; 73 | 74 | close(state->kqfd); 75 | free(state->events); 76 | free(state); 77 | } 78 | 79 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 80 | aeApiState *state = eventLoop->apidata; 81 | struct kevent ke; 82 | 83 | if (mask & ECO_POLL_READABLE) { 84 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 85 | if (kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 86 | } 87 | if (mask & ECO_POLL_WRITABLE) { 88 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 89 | if (kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 90 | } 91 | return 0; 92 | } 93 | 94 | static int aeApiUpdEvent(aeEventLoop *eventLoop, int fd, int mask) { 95 | aeApiState *state = eventLoop->apidata; 96 | struct kevent ke; 97 | 98 | if (mask & ECO_POLL_READABLE) { 99 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 100 | if (kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 101 | } 102 | else { 103 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 104 | kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL); 105 | } 106 | if (mask & ECO_POLL_WRITABLE) { 107 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 108 | if (kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 109 | } 110 | else { 111 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 112 | kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL); 113 | } 114 | return 0; 115 | } 116 | 117 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 118 | aeApiState *state = eventLoop->apidata; 119 | struct kevent ke; 120 | 121 | if (mask & ECO_POLL_READABLE) { 122 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 123 | kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL); 124 | } 125 | if (mask & ECO_POLL_WRITABLE) { 126 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 127 | kevent_f(state->kqfd, &ke, 1, NULL, 0, NULL); 128 | } 129 | } 130 | 131 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 132 | aeApiState *state = eventLoop->apidata; 133 | int retval, numevents = 0; 134 | 135 | if (tvp != NULL) { 136 | struct timespec timeout; 137 | timeout.tv_sec = tvp->tv_sec; 138 | timeout.tv_nsec = tvp->tv_usec * 1000; 139 | retval = kevent_f(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 140 | &timeout); 141 | } else { 142 | retval = kevent_f(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 143 | NULL); 144 | } 145 | 146 | if (retval > 0) { 147 | int j; 148 | 149 | numevents = retval; 150 | for(j = 0; j < numevents; j++) { 151 | int mask = 0; 152 | struct kevent *e = state->events+j; 153 | 154 | if (e->filter == EVFILT_READ) mask |= ECO_POLL_READABLE; 155 | if (e->filter == EVFILT_WRITE) mask |= ECO_POLL_WRITABLE; 156 | eventLoop->fired[j].fd = e->ident; 157 | eventLoop->fired[j].mask = mask; 158 | } 159 | } 160 | return numevents; 161 | } 162 | 163 | static char *aeApiName(void) { 164 | return "kqueue"; 165 | } 166 | 167 | static int aeApiHandle(aeEventLoop* eventLoop) { 168 | aeApiState *state = (aeApiState*)eventLoop->apidata; 169 | return state->kqfd; 170 | } 171 | 172 | #endif //!HAVE_KQUEUE 173 | -------------------------------------------------------------------------------- /src/eco_ae_epoll.c: -------------------------------------------------------------------------------- 1 | /* Linux epoll(2) based ae.c module 2 | * 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifdef HAVE_EPOLL 32 | 33 | #include 34 | 35 | typedef int (*epoll_wait_t)(int epfd, struct epoll_event *events, 36 | int maxevents, int timeout); 37 | extern epoll_wait_t epoll_wait_f; 38 | 39 | typedef struct aeApiState { 40 | int epfd; 41 | struct epoll_event *events; 42 | } aeApiState; 43 | 44 | static int aeApiCreate(aeEventLoop *eventLoop) { 45 | aeApiState *state = malloc(sizeof(aeApiState)); 46 | 47 | if (!state) return -1; 48 | state->events = malloc(sizeof(struct epoll_event)*eventLoop->setsize); 49 | if (!state->events) { 50 | free(state); 51 | return -1; 52 | } 53 | state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */ 54 | if (state->epfd == -1) { 55 | free(state->events); 56 | free(state); 57 | return -1; 58 | } 59 | eventLoop->apidata = state; 60 | return 0; 61 | } 62 | 63 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 64 | aeApiState *state = eventLoop->apidata; 65 | 66 | state->events = realloc(state->events, sizeof(struct epoll_event)*setsize); 67 | return 0; 68 | } 69 | 70 | static void aeApiFree(aeEventLoop *eventLoop) { 71 | aeApiState *state = eventLoop->apidata; 72 | 73 | close(state->epfd); 74 | free(state->events); 75 | free(state); 76 | } 77 | 78 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 79 | aeApiState *state = eventLoop->apidata; 80 | struct epoll_event ee; 81 | /* If the fd was already monitored for some event, we need a MOD 82 | * operation. Otherwise we need an ADD operation. */ 83 | int op = eventLoop->events[fd].mask == ECO_POLL_NONE ? 84 | EPOLL_CTL_ADD : EPOLL_CTL_MOD; 85 | 86 | ee.events = 0; 87 | mask |= eventLoop->events[fd].mask; /* Merge old events */ 88 | if (mask & ECO_POLL_READABLE) ee.events |= EPOLLIN; 89 | if (mask & ECO_POLL_WRITABLE) ee.events |= EPOLLOUT; 90 | ee.data.u64 = 0; /* avoid valgrind warning */ 91 | ee.data.fd = fd; 92 | if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; 93 | return 0; 94 | } 95 | 96 | static int aeApiUpdEvent(aeEventLoop *eventLoop, int fd, int mask) { 97 | aeApiState *state = eventLoop->apidata; 98 | struct epoll_event ee; 99 | /* If the fd was already monitored for some event, we need a MOD 100 | * operation. Otherwise we need an ADD operation. */ 101 | int op = eventLoop->events[fd].mask == ECO_POLL_NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; 102 | 103 | ee.events = 0; 104 | if (mask & ECO_POLL_READABLE) ee.events |= EPOLLIN; 105 | if (mask & ECO_POLL_WRITABLE) ee.events |= EPOLLOUT; 106 | ee.data.u64 = 0; /* avoid valgrind warning */ 107 | ee.data.fd = fd; 108 | if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; 109 | return 0; 110 | } 111 | 112 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { 113 | aeApiState *state = eventLoop->apidata; 114 | struct epoll_event ee; 115 | int mask = eventLoop->events[fd].mask & (~delmask); 116 | 117 | ee.events = 0; 118 | if (mask & ECO_POLL_READABLE) ee.events |= EPOLLIN; 119 | if (mask & ECO_POLL_WRITABLE) ee.events |= EPOLLOUT; 120 | ee.data.u64 = 0; /* avoid valgrind warning */ 121 | ee.data.fd = fd; 122 | if (mask != ECO_POLL_NONE) { 123 | epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee); 124 | } else { 125 | /* Note, Kernel < 2.6.9 requires a non null event pointer even for 126 | * EPOLL_CTL_DEL. */ 127 | epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee); 128 | } 129 | } 130 | 131 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 132 | aeApiState *state = eventLoop->apidata; 133 | int retval, numevents = 0; 134 | 135 | retval = epoll_wait_f(state->epfd,state->events,eventLoop->setsize, 136 | tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); 137 | if (retval > 0) { 138 | int j; 139 | 140 | numevents = retval; 141 | for (j = 0; j < numevents; j++) { 142 | int mask = 0; 143 | struct epoll_event *e = state->events+j; 144 | 145 | if (e->events & EPOLLIN) mask |= ECO_POLL_READABLE; 146 | if (e->events & EPOLLOUT) mask |= ECO_POLL_WRITABLE; 147 | if (e->events & EPOLLERR) mask |= ECO_POLL_WRITABLE; 148 | if (e->events & EPOLLHUP) mask |= ECO_POLL_WRITABLE; 149 | eventLoop->fired[j].fd = e->data.fd; 150 | eventLoop->fired[j].mask = mask; 151 | } 152 | } 153 | return numevents; 154 | } 155 | 156 | static char *aeApiName(void) { 157 | return "epoll"; 158 | } 159 | 160 | static int aeApiHandle(aeEventLoop* eventLoop) { 161 | aeApiState *state = (aeApiState*)eventLoop->apidata; 162 | return state->epfd; 163 | } 164 | 165 | #endif //!HAVE_EPOLL 166 | -------------------------------------------------------------------------------- /src/eco_ae.h: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __ECO_POLL_H__ 34 | #define __ECO_POLL_H__ 35 | 36 | #include "es_types.h" 37 | #include "es_status.h" 38 | #include "es_config.h" 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | //mask 45 | #define ECO_POLL_NONE 0 46 | #define ECO_POLL_READABLE 1 47 | #define ECO_POLL_WRITABLE 2 48 | #define ECO_POLL_ALL_MASKS (ECO_POLL_READABLE|ECO_POLL_WRITABLE) 49 | 50 | //flags 51 | #define ECO_POLL_FILE_EVENTS 1 52 | #define ECO_POLL_TIME_EVENTS 2 53 | #define ECO_POLL_ALL_EVENTS (ECO_POLL_FILE_EVENTS|ECO_POLL_TIME_EVENTS) 54 | 55 | #define ECO_POLL_NOMORE -1 56 | #define ECO_POLL_DELETED_EVENT_ID -1 57 | 58 | typedef struct co_poll_t co_poll_t; 59 | 60 | /* Types and data structures */ 61 | typedef void coFileProc(co_poll_t *poll, int fd, void *clientData, int mask); 62 | typedef int coTimeProc(co_poll_t *poll, es_int64_t id, void *clientData); 63 | typedef void coEventFinalizerProc(co_poll_t *poll, void *clientData); 64 | 65 | /* File event structure */ 66 | typedef struct coFileEvent { 67 | int mask; /* one of ECO_POLL_(READABLE|WRITABLE) */ 68 | coFileProc *rfileProc; 69 | coFileProc *wfileProc; 70 | void *clientData; 71 | } coFileEvent; 72 | 73 | /* Time event structure */ 74 | typedef struct coTimeEvent { 75 | es_int64_t id; /* time event identifier. */ 76 | long when_sec; /* seconds */ 77 | long when_ms; /* milliseconds */ 78 | coTimeProc *timeProc; 79 | coEventFinalizerProc *finalizerProc; 80 | void *clientData; 81 | struct coTimeEvent *next; 82 | } coTimeEvent; 83 | 84 | /* A fired event */ 85 | typedef struct coFiredEvent { 86 | int fd; 87 | int mask; 88 | } coFiredEvent; 89 | 90 | /* State of an event based program */ 91 | struct co_poll_t { 92 | int maxfd; /* highest file descriptor currently registered */ 93 | int setsize; /* max number of file descriptors tracked */ 94 | es_int64_t timeEventNextId; 95 | time_t lastTime; /* Used to detect system clock skew */ 96 | coFileEvent *events; /* Registered events */ 97 | coFiredEvent *fired; /* Fired events */ 98 | coTimeEvent *timeEventHead; 99 | void *apidata; /* This is used for polling API specific data */ 100 | }; 101 | 102 | /* export api */ 103 | co_poll_t* eco_poll_create(int setsize); 104 | void eco_poll_destroy(co_poll_t** ppoll); 105 | 106 | /* Resize the maximum set size of the event loop. 107 | * If the requested set size is smaller than the current set size, but 108 | * there is already a file descriptor in use that is >= the requested 109 | * set size minus one, -1 is returned and the operation is not 110 | * performed at all. 111 | * 112 | * Otherwise 0 is returned and the operation is successful. */ 113 | int eco_poll_resize(co_poll_t *poll, int setsize); 114 | 115 | /* Process every pending time event, then every pending file event 116 | * (that may be registered by time event callbacks just processed). 117 | * Without special flags the function sleeps until some file event 118 | * fires, or when the next time event occurs (if any). 119 | * 120 | * If flags is 0, the function does nothing and returns. 121 | * if flags has ECO_POLL_ALL_EVENTS set, all the kind of events are processed. 122 | * if flags has ECO_POLL_FILE_EVENTS set, file events are processed. 123 | * if flags has ECO_POLL_TIME_EVENTS set, time events are processed. 124 | * 125 | * timeout >0 wait some time | ==0 ASAP | <0 block indefinitely 126 | * 127 | * The function returns the number of events processed. */ 128 | int eco_poll_process_events(co_poll_t* poll, int flags, es_int64_t timeout); 129 | 130 | es_status_t eco_poll_file_event_create(co_poll_t* poll, int fd, int mask, 131 | coFileProc *proc, void *clientData); 132 | es_status_t eco_poll_file_event_update(co_poll_t *poll, int fd, int mask, 133 | coFileProc *proc, void *clientData); 134 | void eco_poll_file_event_delete(co_poll_t* poll, int fd, int mask); 135 | int eco_poll_get_file_events(co_poll_t* poll, int fd); 136 | 137 | es_int64_t eco_poll_time_event_create(co_poll_t* poll, es_int64_t milliseconds, 138 | coTimeProc *proc, void *clientData, 139 | coEventFinalizerProc *finalizerProc); 140 | es_status_t eco_poll_time_event_delete(co_poll_t* poll, es_int64_t id); 141 | 142 | /* This api is only for fiber mode! */ 143 | es_status_t eco_poll_time_fire_all(co_poll_t* poll); 144 | 145 | /* Wait for milliseconds until the given file descriptor becomes 146 | * writable/readable/exception */ 147 | int eco_poll_wait(int fd, int mask, es_int64_t milliseconds); 148 | 149 | /* Return the current poll type. */ 150 | char *eco_poll_api_name(void); 151 | 152 | /* Return the current set size. */ 153 | int eco_poll_get_size(co_poll_t *poll); 154 | 155 | /* Return native poll fd. */ 156 | int eco_poll_getfd(co_poll_t* poll); 157 | 158 | #ifdef __cplusplus 159 | } 160 | #endif 161 | 162 | #endif //!__ECO_POLL_H__ 163 | -------------------------------------------------------------------------------- /test/benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include "es_main.h" 2 | #include "Eco.hh" 3 | 4 | #define LOG(fmt,...) ESystem::out->printfln(fmt, ##__VA_ARGS__) 5 | 6 | //============================================================================= 7 | //linux: per second op times: 10638297.872340 8 | //os x : per second op times: 14492753.623188 9 | 10 | static void test_scheduling_performance() { 11 | #ifdef CPP11_SUPPORT 12 | EFiberDebugger::getInstance().debugOn(EFiberDebugger::NONE); 13 | 14 | EFiberScheduler scheduler; 15 | 16 | llong t1 = ESystem::currentTimeMillis(); 17 | 18 | int times = 1000000; 19 | 20 | #if 1 21 | int count = times; 22 | 23 | for (int i=0; i<10; i++) { 24 | scheduler.schedule([&]() { 25 | while (count-- > 0) { 26 | EFiber::yield(); 27 | } 28 | }); 29 | } 30 | 31 | scheduler.join(); 32 | #else 33 | EAtomicCounter count(times); 34 | 35 | for (int i=0; i<10; i++) { 36 | scheduler.schedule([&]() { 37 | while (count-- > 0) { 38 | EFiber::yield(); 39 | } 40 | }); 41 | } 42 | 43 | scheduler.join(4); 44 | #endif 45 | 46 | llong t2 = ESystem::currentTimeMillis(); 47 | 48 | LOG("switch 10 fibers run %ld times, cost %ld ms\nper second op times: %f", times, t2 - t1, ((double)times)/(t2-t1)*1000); 49 | #endif 50 | } 51 | 52 | //============================================================================= 53 | //wrk -t12 -c100 -d30s -T30s -H "Connection: close" http://127.0.0.1:9988/ 54 | //linux: Requests/sec: 246052 55 | //os x : Requests/sec: 53310 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #ifdef __linux__ 63 | #include 64 | #endif 65 | 66 | static const char* g_ip = "0.0.0.0"; 67 | static const uint16_t g_port = 8888; 68 | static int thread_count = 8; 69 | static int tps = 0; 70 | 71 | static inline int atomic_add32(volatile int * mem, int val) 72 | { 73 | int ret; 74 | asm volatile 75 | ( 76 | "lock\n\t xadd %1, %0" 77 | : "+m"( *mem ), "=r"( ret ) 78 | : "1"( val ) 79 | : "memory", "cc" 80 | ); 81 | 82 | return ret; 83 | } 84 | 85 | static inline int do_write(int fd, const char * b, int l) 86 | { 87 | int w = 0, t = 0; 88 | for (;;) { 89 | t = ::write(fd, b + w, l - w); 90 | if (t < 1) { 91 | // LOG("%s:%u, msg=%s", __FUNCTION__, __LINE__, strerror(errno)); 92 | return -1; 93 | } 94 | w += t; 95 | if (l == w) return l; 96 | } 97 | 98 | return -1; 99 | } 100 | 101 | static inline int do_read(int fd, char * b, int l) 102 | { 103 | int r = 0, t = 0; 104 | for (;;) { 105 | t = ::read(fd, b + r, l - r); 106 | if (t < 1) { 107 | // LOG("%s:%u, msg=%s", __FUNCTION__, __LINE__, strerror(errno)); 108 | return -1; 109 | } 110 | r += t; 111 | return r; 112 | } 113 | 114 | return -1; 115 | } 116 | 117 | static void server(EFiberScheduler& scheduler) 118 | { 119 | int ret; 120 | int accept_fd = socket(AF_INET, SOCK_STREAM, 0); 121 | assert(accept_fd >= 0); 122 | 123 | #ifdef __APPLE__ 124 | int v = 1; 125 | ret = setsockopt(accept_fd, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)); 126 | #else 127 | #if LINUX_VERSION_CODE>= KERNEL_VERSION(3,9,0) 128 | int v = 1; 129 | ret = setsockopt(accept_fd, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)); 130 | #endif 131 | #endif 132 | 133 | sockaddr_in addr; 134 | addr.sin_family = AF_INET; 135 | addr.sin_port = htons(g_port); 136 | addr.sin_addr.s_addr = inet_addr(g_ip); 137 | ret = bind(accept_fd, (sockaddr*)&addr, sizeof(addr)); 138 | assert(ret == 0); 139 | 140 | ret = listen(accept_fd, 8192); 141 | assert(ret == 0); 142 | for (;;) { 143 | socklen_t addr_len = sizeof(addr); 144 | int s = accept(accept_fd, (sockaddr*)&addr, &addr_len); 145 | if (s < 0) { 146 | perror("accept error:"); 147 | continue; 148 | } 149 | 150 | int size = 256 * 1024; 151 | setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof(size)); 152 | 153 | int flag = 1; 154 | setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); 155 | 156 | struct linger li = { 1, 0 }; 157 | setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li)); 158 | 159 | scheduler.schedule([s]() { 160 | while (1) { 161 | int rsize = 1024; 162 | char rbuf[rsize]; 163 | 164 | ssize_t rn = do_read(s, rbuf, rsize); 165 | if (rn < 0) { 166 | shutdown(s, 0x02); 167 | close(s); 168 | break; 169 | } 170 | 171 | memcpy(rbuf, "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/html\r\n\r\nHello,world", 75); 172 | rsize = 75; 173 | rn = do_write(s, rbuf, rsize); 174 | if (rn < 0) { 175 | shutdown(s, 0x02); 176 | close(s); 177 | return ; 178 | } 179 | 180 | atomic_add32(&tps, 1); 181 | } 182 | }); 183 | } 184 | } 185 | 186 | static void show_status() 187 | { 188 | while (1) { 189 | printf("tps : %d\n", atomic_add32(&tps, -1 * tps)); 190 | sleep(1); 191 | } 192 | } 193 | 194 | static int balance_callback(EFiber* fiber, int threadNums) { 195 | int fid = fiber->getId(); 196 | if (fid == 0) { // 0 is the first fiber. 197 | return 0; // 0 is the join()'s thread 198 | } else { 199 | return fid % (threadNums - 1) + 1; // balance to other's threads. 200 | } 201 | } 202 | 203 | #define SET_RLIMIT 0 204 | #define USE_SIG_BLOCK 0 205 | 206 | static void test_iohooking_performance() { 207 | #if SET_RLIMIT 208 | rlimit of = {1000000, 1000000}; 209 | if (-1 == setrlimit(RLIMIT_NOFILE, &of)) { 210 | perror("setrlimit"); 211 | exit(1); 212 | } 213 | #endif 214 | 215 | #if USE_SIG_BLOCK 216 | sigset_t new_mask, old_mask, wait_mask; 217 | sigfillset(&new_mask); 218 | pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask); 219 | #endif 220 | 221 | sp thd = EThread::executeX([](){ 222 | show_status(); 223 | }); 224 | 225 | EFiberScheduler scheduler; 226 | scheduler.schedule([&](){ 227 | server(scheduler); 228 | }); 229 | scheduler.join(thread_count); 230 | // scheduler.join(thread_count, balance_callback); 231 | thd->join(); 232 | 233 | #if USE_SIG_BLOCK 234 | pthread_sigmask(SIG_SETMASK, &old_mask, 0); 235 | sigemptyset(&wait_mask); 236 | sigaddset(&wait_mask, SIGINT); 237 | sigaddset(&wait_mask, SIGQUIT); 238 | sigaddset(&wait_mask, SIGTERM); 239 | sigaddset(&wait_mask, SIGUSR1); 240 | sigaddset(&wait_mask, SIGUSR2); 241 | pthread_sigmask(SIG_BLOCK, &wait_mask, 0); 242 | #endif 243 | } 244 | 245 | MAIN_IMPL(testeco_benchmark) { 246 | printf("main()\n"); 247 | 248 | ESystem::init(argc, argv); 249 | 250 | printf("inited.\n"); 251 | 252 | int i = 0; 253 | try { 254 | boolean loop = EBoolean::parseBoolean(ESystem::getProgramArgument("loop")); 255 | 256 | // EFiberDebugger::getInstance().debugOn(EFiberDebugger::SCHEDULER); 257 | 258 | do { 259 | // test_scheduling_performance(); 260 | test_iohooking_performance(); 261 | } while (1); 262 | } 263 | catch (EException& e) { 264 | e.printStackTrace(); 265 | } 266 | catch (...) { 267 | printf("catch all...\n"); 268 | } 269 | 270 | printf("exit...\n"); 271 | 272 | ESystem::exit(0); 273 | 274 | return 0; 275 | } 276 | -------------------------------------------------------------------------------- /test/echoserver.cpp: -------------------------------------------------------------------------------- 1 | #include "es_main.h" 2 | #include "Eco.hh" 3 | 4 | #include 5 | 6 | #define LOG(fmt,...) ESystem::out->printfln(fmt, ##__VA_ARGS__) 7 | 8 | #define FIBER_THREADS 4 9 | #define MAX_CONNECTIONS 2 10 | #define MAX_IDLETIME 60000 11 | EFiberScheduler scheduler; 12 | struct ThreadData: public EObject { 13 | struct Sock: public EObject { 14 | sp socket; 15 | long lastActiveTime; 16 | 17 | Sock(sp s, long t): socket(s), lastActiveTime(t) {} 18 | }; 19 | EHashMap sockMap; 20 | 21 | void updateLastActiveTime(sp socket, long time) { 22 | Sock* sock = sockMap.get(socket->getFD()); 23 | if (sock) { 24 | sock->lastActiveTime = time; 25 | } 26 | } 27 | }; 28 | 29 | static EA gThreadData(FIBER_THREADS, false); 30 | 31 | static volatile int gStopFlag = 0; 32 | 33 | static EAtomicCounter gConnections; 34 | 35 | static void SigHandler(int sig) { 36 | scheduler.interrupt(); 37 | gStopFlag = 1; 38 | } 39 | 40 | static int balance_callback(EFiber* fiber, int threadNums) { 41 | int fid = fiber->getId(); 42 | if (fid == 0) { // 0 is the first fiber. 43 | return 0; // 0 is the join()'s thread 44 | } else { 45 | long id = fiber->getTag(); 46 | if (id > 0) { 47 | return (int)id; 48 | } else { 49 | return fid % (threadNums - 1) + 1; // balance to other's threads. 50 | } 51 | } 52 | } 53 | 54 | static void schedule_callback(int threadIndex, 55 | EFiberScheduler::SchedulePhase schedulePhase, EThread* currentThread, 56 | EFiber* currentFiber) { 57 | switch (schedulePhase) { 58 | case EFiberScheduler::SCHEDULE_BEFORE: { 59 | LOG("SCHEDULE_BEFORE index=%d", threadIndex); 60 | gThreadData[threadIndex] = new ThreadData(); 61 | break; 62 | } 63 | case EFiberScheduler::SCHEDULE_AFTER: { 64 | LOG("SCHEDULE_AFTER index=%d", threadIndex); 65 | delete gThreadData[threadIndex]; 66 | break; 67 | } 68 | default: 69 | // LOG("index=%d, phase=%d, thread=%d, fiber=%d", 70 | // threadIndex, schedulePhase, currentThread->getId(), 71 | // currentFiber ? currentFiber->getId() : -1); 72 | break; 73 | } 74 | } 75 | 76 | MAIN_IMPL(testeco_echoserver) { 77 | printf("main()\n"); 78 | 79 | // eso_proc_detach(TRUE, 0, 0, 0); 80 | 81 | ESystem::init(argc, argv); 82 | 83 | eso_signal(SIGINT, SigHandler); 84 | 85 | printf("inited.\n"); 86 | 87 | // rlimit of = {100000, 100000}; 88 | // if (-1 == setrlimit(RLIMIT_NOFILE, &of)) { 89 | // perror("setrlimit"); 90 | // exit(1); 91 | // } 92 | 93 | try { 94 | // EFiberScheduler scheduler; 95 | 96 | scheduler.schedule([&](){ 97 | EServerSocket ss; 98 | ss.setReuseAddress(true); 99 | ss.bind(8888, 8192); 100 | ss.setSoTimeout(30000); 101 | 102 | while (!gStopFlag) { 103 | try { 104 | // accept 105 | sp socket = ss.accept(); 106 | if (socket != null) { 107 | // reach the max connections. 108 | if (gConnections.value() >= MAX_CONNECTIONS) { 109 | socket->close(); 110 | EThread::yield(); //? 111 | continue; 112 | } 113 | 114 | scheduler.schedule([=](){ 115 | gConnections++; 116 | 117 | try { 118 | socket->setTcpNoDelay(true); 119 | socket->setSoLinger(true, 0); 120 | socket->setReceiveBufferSize(256*1024); 121 | socket->setSoTimeout(30000); 122 | 123 | // update accept time 124 | ThreadData* threadData = gThreadData[EFiber::currentFiber()->getThreadIndex()]; 125 | threadData->sockMap.put(socket->getFD(), new ThreadData::Sock(socket, ESystem::currentTimeMillis())); 126 | 127 | char buf[512] = {0}; 128 | // read 129 | EInputStream* is = socket->getInputStream(); 130 | EOutputStream* os = socket->getOutputStream(); 131 | 132 | while (!gStopFlag) { 133 | try { 134 | // read 135 | int n = is->read(buf, sizeof(buf)); 136 | if (n == -1) { //EOF 137 | threadData->sockMap.remove(socket->getFD()); 138 | socket->close(); 139 | LOG("socket closed"); 140 | break; 141 | } else { 142 | // update read time 143 | threadData->updateLastActiveTime(socket, ESystem::currentTimeMillis()); 144 | 145 | LOG("read buf=%s", buf); 146 | 147 | // write 148 | // os->write(buf, n); 149 | os->write("HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/html\r\n\r\nHello,world", 75); 150 | } 151 | } catch (ESocketTimeoutException& e) { 152 | LOG("read timeout"); 153 | } 154 | 155 | #if 0 156 | socket->close(); 157 | break; 158 | #endif 159 | } 160 | } catch (EIOException& e) { 161 | e.printStackTrace(); 162 | } catch (...) { 163 | } 164 | 165 | gConnections--; 166 | }); 167 | } 168 | } catch (ESocketTimeoutException& e) { 169 | LOG("accept timeout"); 170 | } 171 | } 172 | }/*, 32*1024*/); 173 | 174 | // create leader fiber per-thread for idle socket clean. 175 | for (int i=1; i workThreadLeadFiber = scheduler.schedule([&](){ 177 | LOG("I'm leader fiber. thread id=%ld", EThread::currentThread()->getId()); 178 | 179 | try { 180 | ThreadData* threadData = gThreadData[EFiber::currentFiber()->getThreadIndex()]; 181 | 182 | while (!gStopFlag) { 183 | long currTime = ESystem::currentTimeMillis(); 184 | sp > iter = threadData->sockMap.values()->iterator(); 185 | while (iter->hasNext()) { 186 | ThreadData::Sock* sock = iter->next(); 187 | 188 | if (currTime - sock->lastActiveTime > MAX_IDLETIME) { 189 | //shutdown socket by server. 190 | LOG("socket shutdown1"); 191 | sock->socket->shutdown(); 192 | LOG("socket shutdown2"); 193 | } 194 | } 195 | 196 | sleep(3); 197 | } 198 | } catch (EThrowable& t) { 199 | t.printStackTrace(); 200 | } 201 | 202 | }); 203 | workThreadLeadFiber->setTag(i); 204 | } 205 | 206 | // schedule callback 207 | scheduler.setScheduleCallback(schedule_callback); 208 | 209 | // balance callback 210 | #ifdef CPP11_SUPPORT 211 | scheduler.setBalanceCallback([](EFiber* fiber, int threadNums){ 212 | int fid = fiber->getId(); 213 | if (fid == 0) { // 0 is the first fiber. 214 | return 0; // 0 is the join()'s thread 215 | } else { 216 | long id = fiber->getTag(); 217 | if (id > 0) { 218 | return (int)id; 219 | } else { 220 | return fid % (threadNums - 1) + 1; // balance to other's threads. 221 | } 222 | } 223 | }); 224 | #else 225 | scheduler.setBalanceCallback(balance_callback); 226 | #endif 227 | 228 | scheduler.join(FIBER_THREADS); 229 | } 230 | catch (EException& e) { 231 | e.printStackTrace(); 232 | } 233 | catch (...) { 234 | printf("catch all...\n"); 235 | } 236 | 237 | printf("exit...\n"); 238 | 239 | ESystem::exit(0); 240 | 241 | return 0; 242 | } 243 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/elf_hook.c: -------------------------------------------------------------------------------- 1 | #ifdef __linux__ 2 | 3 | #include "elf_hook.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | //rename standart types for convenience 15 | #ifdef __x86_64 16 | #define Elf_Ehdr Elf64_Ehdr 17 | #define Elf_Shdr Elf64_Shdr 18 | #define Elf_Sym Elf64_Sym 19 | #define Elf_Rel Elf64_Rela 20 | #define ELF_R_SYM ELF64_R_SYM 21 | #define REL_DYN ".rela.dyn" 22 | #define REL_PLT ".rela.plt" 23 | #else 24 | #define Elf_Ehdr Elf32_Ehdr 25 | #define Elf_Shdr Elf32_Shdr 26 | #define Elf_Sym Elf32_Sym 27 | #define Elf_Rel Elf32_Rel 28 | #define ELF_R_SYM ELF32_R_SYM 29 | #define REL_DYN ".rel.dyn" 30 | #define REL_PLT ".rel.plt" 31 | #endif 32 | 33 | //================================================================================================== 34 | static int read_header(int d, Elf_Ehdr **header) 35 | { 36 | *header = (Elf_Ehdr *)malloc(sizeof(Elf_Ehdr)); 37 | if(NULL == *header) 38 | { 39 | return errno; 40 | } 41 | 42 | if (lseek(d, 0, SEEK_SET) < 0) 43 | { 44 | free(*header); 45 | 46 | return errno; 47 | } 48 | 49 | if (read(d, *header, sizeof(Elf_Ehdr)) <= 0) 50 | { 51 | free(*header); 52 | 53 | return errno = EINVAL; 54 | } 55 | 56 | return 0; 57 | } 58 | //-------------------------------------------------------------------------------------------------- 59 | static int read_section_table(int d, Elf_Ehdr const *header, Elf_Shdr **table) 60 | { 61 | size_t size; 62 | 63 | if (NULL == header) 64 | return EINVAL; 65 | 66 | size = header->e_shnum * sizeof(Elf_Shdr); 67 | *table = (Elf_Shdr *)malloc(size); 68 | if(NULL == *table) 69 | { 70 | return errno; 71 | } 72 | 73 | if (lseek(d, header->e_shoff, SEEK_SET) < 0) 74 | { 75 | free(*table); 76 | 77 | return errno; 78 | } 79 | 80 | if (read(d, *table, size) <= 0) 81 | { 82 | free(*table); 83 | 84 | return errno = EINVAL; 85 | } 86 | 87 | return 0; 88 | } 89 | //-------------------------------------------------------------------------------------------------- 90 | static int read_string_table(int d, Elf_Shdr const *section, char const **strings) 91 | { 92 | if (NULL == section) 93 | return EINVAL; 94 | 95 | *strings = (char const *)malloc(section->sh_size); 96 | if(NULL == *strings) 97 | { 98 | return errno; 99 | } 100 | 101 | if (lseek(d, section->sh_offset, SEEK_SET) < 0) 102 | { 103 | free((void *)*strings); 104 | 105 | return errno; 106 | } 107 | 108 | if (read(d, (char *)*strings, section->sh_size) <= 0) 109 | { 110 | free((void *)*strings); 111 | 112 | return errno = EINVAL; 113 | } 114 | 115 | return 0; 116 | } 117 | //-------------------------------------------------------------------------------------------------- 118 | static int read_symbol_table(int d, Elf_Shdr const *section, Elf_Sym **table) 119 | { 120 | if (NULL == section) 121 | return EINVAL; 122 | 123 | *table = (Elf_Sym *)malloc(section->sh_size); 124 | if(NULL == *table) 125 | { 126 | return errno; 127 | } 128 | 129 | if (lseek(d, section->sh_offset, SEEK_SET) < 0) 130 | { 131 | free(*table); 132 | 133 | return errno; 134 | } 135 | 136 | if (read(d, *table, section->sh_size) <= 0) 137 | { 138 | free(*table); 139 | 140 | return errno = EINVAL; 141 | } 142 | 143 | return 0; 144 | } 145 | //-------------------------------------------------------------------------------------------------- 146 | static int read_relocation_table(int d, Elf_Shdr const *section, Elf_Rel **table) 147 | { 148 | if (NULL == section) 149 | return EINVAL; 150 | 151 | *table = (Elf_Rel *)malloc(section->sh_size); 152 | if(NULL == *table) 153 | { 154 | return errno; 155 | } 156 | 157 | if (lseek(d, section->sh_offset, SEEK_SET) < 0) 158 | { 159 | free(*table); 160 | 161 | return errno; 162 | } 163 | 164 | if (read(d, *table, section->sh_size) <= 0) 165 | { 166 | free(*table); 167 | 168 | return errno = EINVAL; 169 | } 170 | 171 | return 0; 172 | } 173 | //-------------------------------------------------------------------------------------------------- 174 | static int section_by_index(int d, size_t const index, Elf_Shdr **section) 175 | { 176 | Elf_Ehdr *header = NULL; 177 | Elf_Shdr *sections = NULL; 178 | size_t i; 179 | 180 | *section = NULL; 181 | 182 | if ( 183 | read_header(d, &header) || 184 | read_section_table(d, header, §ions) 185 | ) 186 | return errno; 187 | 188 | if (index < header->e_shnum) 189 | { 190 | *section = (Elf_Shdr *)malloc(sizeof(Elf_Shdr)); 191 | 192 | if (NULL == *section) 193 | { 194 | free(header); 195 | free(sections); 196 | 197 | return errno; 198 | } 199 | 200 | memcpy(*section, sections + index, sizeof(Elf_Shdr)); 201 | } 202 | else 203 | return EINVAL; 204 | 205 | free(header); 206 | free(sections); 207 | 208 | return 0; 209 | } 210 | //-------------------------------------------------------------------------------------------------- 211 | static int section_by_type(int d, size_t const section_type, Elf_Shdr **section) 212 | { 213 | Elf_Ehdr *header = NULL; 214 | Elf_Shdr *sections = NULL; 215 | size_t i; 216 | 217 | *section = NULL; 218 | 219 | if ( 220 | read_header(d, &header) || 221 | read_section_table(d, header, §ions) 222 | ) 223 | return errno; 224 | 225 | for (i = 0; i < header->e_shnum; ++i) 226 | if (section_type == sections[i].sh_type) 227 | { 228 | *section = (Elf_Shdr *)malloc(sizeof(Elf_Shdr)); 229 | 230 | if (NULL == *section) 231 | { 232 | free(header); 233 | free(sections); 234 | 235 | return errno; 236 | } 237 | 238 | memcpy(*section, sections + i, sizeof(Elf_Shdr)); 239 | 240 | break; 241 | } 242 | 243 | free(header); 244 | free(sections); 245 | 246 | return 0; 247 | } 248 | //-------------------------------------------------------------------------------------------------- 249 | static int section_by_name(int d, char const *section_name, Elf_Shdr **section) 250 | { 251 | Elf_Ehdr *header = NULL; 252 | Elf_Shdr *sections = NULL; 253 | char const *strings = NULL; 254 | size_t i; 255 | 256 | *section = NULL; 257 | 258 | if ( 259 | read_header(d, &header) || 260 | read_section_table(d, header, §ions) || 261 | read_string_table(d, §ions[header->e_shstrndx], &strings) 262 | ) 263 | return errno; 264 | 265 | for (i = 0; i < header->e_shnum; ++i) 266 | if (!strcmp(section_name, &strings[sections[i].sh_name])) 267 | { 268 | *section = (Elf_Shdr *)malloc(sizeof(Elf_Shdr)); 269 | 270 | if (NULL == *section) 271 | { 272 | free(header); 273 | free(sections); 274 | free((void *)strings); 275 | 276 | return errno; 277 | } 278 | 279 | memcpy(*section, sections + i, sizeof(Elf_Shdr)); 280 | 281 | break; 282 | } 283 | 284 | free(header); 285 | free(sections); 286 | free((void *)strings); 287 | 288 | return 0; 289 | } 290 | 291 | #ifdef __cplusplus 292 | extern "C" 293 | { 294 | #endif 295 | 296 | int rebind_symbols_image(char const *module_filename, void *handle, 297 | struct rebinding rebindings[], size_t rebindings_nel) 298 | { 299 | int descriptor; //file descriptor of shared module 300 | 301 | Elf_Shdr 302 | *dynsym = NULL, // ".dynsym" section header 303 | *rel_plt = NULL, // ".rel.plt" section header 304 | *rel_dyn = NULL; // ".rel.dyn" section header 305 | 306 | Elf_Rel 307 | *rel_plt_table = NULL, //array with ".rel.plt" entries 308 | *rel_dyn_table = NULL; //array with ".rel.dyn" entries 309 | 310 | size_t 311 | i, 312 | rel_plt_amount, // amount of ".rel.plt" entries 313 | rel_dyn_amount, // amount of ".rel.dyn" entries 314 | *name_address = NULL; //address of relocation for symbol named "name" 315 | 316 | void *original = NULL; //address of the symbol being substituted 317 | 318 | void *module_address = NULL; 319 | 320 | Elf_Shdr *strings_section = NULL; 321 | char const *strings = NULL; 322 | Elf_Sym *symbols = NULL; 323 | size_t amount; 324 | Elf_Sym *found = NULL; 325 | 326 | if (NULL == module_filename || NULL == handle) 327 | return -1; 328 | 329 | descriptor = open(module_filename, O_RDONLY); 330 | 331 | if (descriptor < 0) 332 | return -1; 333 | 334 | // get base address 335 | if (section_by_type(descriptor, SHT_DYNSYM, &dynsym) || //get ".dynsym" section 336 | section_by_index(descriptor, dynsym->sh_link, &strings_section) || 337 | read_string_table(descriptor, strings_section, &strings) || 338 | read_symbol_table(descriptor, dynsym, &symbols)) 339 | { 340 | free(strings_section); 341 | free((void *)strings); 342 | free(symbols); 343 | free(dynsym); 344 | 345 | close(descriptor); 346 | 347 | return errno; 348 | } 349 | 350 | amount = dynsym->sh_size / sizeof(Elf_Sym); 351 | 352 | /* Trick to get the module base address in a portable way: 353 | * Find the first GLOBAL or WEAK symbol in the symbol table, 354 | * look this up with dlsym, then return the difference as the base address 355 | */ 356 | for (i = 0; i < amount; ++i) 357 | { 358 | switch(ELF32_ST_BIND(symbols[i].st_info)) { 359 | case STB_GLOBAL: 360 | case STB_WEAK: 361 | found = &symbols[i]; 362 | break; 363 | default: // Not interested in this symbol 364 | break; 365 | } 366 | } 367 | if (found != NULL) 368 | { 369 | const char *name = &strings[found->st_name]; 370 | void *sym = dlsym(handle, name); 371 | if(sym != NULL) 372 | module_address = (void*)((size_t)sym - found->st_value); 373 | } 374 | 375 | free(strings_section); 376 | free((void *)strings); 377 | free(dynsym); 378 | 379 | if (!module_address) { 380 | free(symbols); 381 | close(descriptor); 382 | return -1; 383 | } 384 | 385 | if ( 386 | section_by_name(descriptor, REL_PLT, &rel_plt) || //get ".rel.plt" (for 32-bit) or ".rela.plt" (for 64-bit) section 387 | section_by_name(descriptor, REL_DYN, &rel_dyn) //get ".rel.dyn" (for 32-bit) or ".rela.dyn" (for 64-bit) section 388 | ) 389 | { //if something went wrong 390 | free(rel_plt); 391 | free(rel_dyn); 392 | free(symbols); 393 | close(descriptor); 394 | return -1; 395 | } 396 | 397 | close(descriptor); 398 | 399 | rel_plt_table = (Elf_Rel *)(((size_t)module_address) + rel_plt->sh_addr); //init the ".rel.plt" array 400 | rel_plt_amount = rel_plt->sh_size / sizeof(Elf_Rel); //and get its size 401 | 402 | rel_dyn_table = (Elf_Rel *)(((size_t)module_address) + rel_dyn->sh_addr); //init the ".rel.dyn" array 403 | rel_dyn_amount = rel_dyn->sh_size / sizeof(Elf_Rel); //and get its size 404 | 405 | //release the data used 406 | free(rel_plt); 407 | free(rel_dyn); 408 | 409 | //now we've got ".rel.plt" (needed for PIC) table and ".rel.dyn" (for non-PIC) table and the symbol's index 410 | for (i = 0; i < rel_plt_amount; ++i) //lookup the ".rel.plt" table 411 | { 412 | uint j; 413 | uint16_t ndx = ELF_R_SYM(rel_plt_table[i].r_info); 414 | char* name = (char*)((size_t)module_address + symbols[ndx].st_name); 415 | for (j = 0; j < rebindings_nel; j++) { 416 | if (strcmp(name, rebindings[j].name) == 0) { 417 | if (rebindings[j].replaced != NULL) { 418 | original = (void *)*(size_t *)(((size_t)module_address) + rel_plt_table[i].r_offset); //save the original function address 419 | *(rebindings[j].replaced) = original; 420 | } 421 | *(size_t *)(((size_t)module_address) + rel_plt_table[i].r_offset) = (size_t)rebindings[j].replacement; //and replace it with the substitutional 422 | break; //the target symbol appears in ".rel.plt" only once 423 | } 424 | } 425 | } 426 | 427 | free(symbols); 428 | 429 | return 0; 430 | } 431 | #ifdef __cplusplus 432 | } 433 | #endif 434 | 435 | #endif //#!__linux__ 436 | -------------------------------------------------------------------------------- /src/eco_ae.c: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "eco_ae.h" 44 | 45 | #define HAVE_POLL 46 | 47 | #define aeEventLoop co_poll_t 48 | 49 | /* Include the best multiplexing layer supported by this system. 50 | * The following should be ordered by performances, descending. */ 51 | #ifdef HAVE_EPOLL 52 | #include "eco_ae_epoll.c" 53 | #else 54 | #ifdef HAVE_KQUEUE 55 | #include "eco_ae_kqueue.c" 56 | #else //win etc. 57 | #include "eco_ae_select.c" 58 | #endif 59 | #endif 60 | 61 | co_poll_t *eco_poll_create(int setsize) { 62 | co_poll_t *poll; 63 | int i; 64 | 65 | if ((poll = malloc(sizeof(*poll))) == NULL) goto err; 66 | poll->events = malloc(sizeof(coFileEvent)*setsize); 67 | poll->fired = malloc(sizeof(coFiredEvent)*setsize); 68 | if (poll->events == NULL || poll->fired == NULL) goto err; 69 | poll->setsize = setsize; 70 | poll->lastTime = time(NULL); 71 | poll->timeEventHead = NULL; 72 | poll->timeEventNextId = 0; 73 | poll->maxfd = -1; 74 | if (aeApiCreate(poll) == -1) goto err; 75 | /* Events with mask == ECO_POLL_NONE are not set. So let's initialize the 76 | * vector with it. */ 77 | for (i = 0; i < setsize; i++) 78 | poll->events[i].mask = ECO_POLL_NONE; 79 | return poll; 80 | 81 | err: 82 | if (poll) { 83 | free(poll->events); 84 | free(poll->fired); 85 | free(poll); 86 | } 87 | return NULL; 88 | } 89 | 90 | void eco_poll_destroy(co_poll_t** poll) { 91 | if (!poll || !*poll) { 92 | return; 93 | } 94 | aeApiFree(*poll); 95 | free((*poll)->events); 96 | free((*poll)->fired); 97 | free(*poll); 98 | *poll = NULL; 99 | } 100 | 101 | /* Return the current set size. */ 102 | int eco_poll_get_size(co_poll_t *poll) { 103 | return poll->setsize; 104 | } 105 | 106 | /* Resize the maximum set size of the event loop. 107 | * If the requested set size is smaller than the current set size, but 108 | * there is already a file descriptor in use that is >= the requested 109 | * set size minus one, -1 is returned and the operation is not 110 | * performed at all. 111 | * 112 | * Otherwise 0 is returned and the operation is successful. */ 113 | int eco_poll_resize(co_poll_t *poll, int setsize) { 114 | int i; 115 | 116 | if (setsize == poll->setsize) return 0; 117 | if (poll->maxfd >= setsize) return -1; 118 | if (aeApiResize(poll,setsize) == -1) return -1; 119 | 120 | poll->events = realloc(poll->events,sizeof(coFileEvent)*setsize); 121 | poll->fired = realloc(poll->fired,sizeof(coFiredEvent)*setsize); 122 | poll->setsize = setsize; 123 | 124 | /* Make sure that if we created new slots, they are initialized with 125 | * an ECO_POLL_NONE mask. */ 126 | for (i = poll->maxfd+1; i < setsize; i++) 127 | poll->events[i].mask = ECO_POLL_NONE; 128 | return 0; 129 | } 130 | 131 | es_status_t eco_poll_file_event_create(co_poll_t *poll, int fd, int mask, 132 | coFileProc *proc, void *clientData) 133 | { 134 | coFileEvent *fe = NULL; 135 | 136 | if (fd >= poll->setsize) { 137 | errno = ERANGE; 138 | return ES_FAILURE; 139 | } 140 | fe = &poll->events[fd]; 141 | 142 | if (aeApiAddEvent(poll, fd, mask) == -1) 143 | return ES_FAILURE; 144 | fe->mask |= mask; 145 | if (mask & ECO_POLL_READABLE) fe->rfileProc = proc; 146 | if (mask & ECO_POLL_WRITABLE) fe->wfileProc = proc; 147 | fe->clientData = clientData; 148 | if (fd > poll->maxfd) 149 | poll->maxfd = fd; 150 | return ES_SUCCESS; 151 | } 152 | 153 | es_status_t eco_poll_file_event_update(co_poll_t *poll, int fd, int mask, 154 | coFileProc *proc, void *clientData) 155 | { 156 | coFileEvent *fe = NULL; 157 | 158 | if (fd >= poll->setsize) { 159 | errno = ERANGE; 160 | return ES_FAILURE; 161 | } 162 | fe = &poll->events[fd]; 163 | 164 | if (aeApiUpdEvent(poll, fd, mask) == -1) 165 | return ES_FAILURE; 166 | fe->mask |= mask; 167 | if (mask & ECO_POLL_READABLE) 168 | fe->rfileProc = proc; 169 | if (mask & ECO_POLL_WRITABLE) 170 | fe->wfileProc = proc; 171 | fe->clientData = clientData; 172 | if (fd > poll->maxfd) 173 | poll->maxfd = fd; 174 | return ES_SUCCESS; 175 | } 176 | 177 | void eco_poll_file_event_delete(co_poll_t *poll, int fd, int mask) 178 | { 179 | coFileEvent *fe = NULL; 180 | 181 | if (fd >= poll->setsize) return; 182 | fe = &poll->events[fd]; 183 | 184 | if (fe->mask == ECO_POLL_NONE) return; 185 | fe->mask = fe->mask & (~mask); 186 | if (fd == poll->maxfd && fe->mask == ECO_POLL_NONE) { 187 | /* Update the max fd */ 188 | int j; 189 | 190 | for (j = poll->maxfd-1; j >= 0; j--) 191 | if (poll->events[j].mask != ECO_POLL_NONE) break; 192 | poll->maxfd = j; 193 | } 194 | aeApiDelEvent(poll, fd, mask); 195 | } 196 | 197 | int eco_poll_get_file_events(co_poll_t *poll, int fd) { 198 | coFileEvent *fe = NULL; 199 | 200 | if (fd >= poll->setsize) return 0; 201 | fe = &poll->events[fd]; 202 | 203 | return fe->mask; 204 | } 205 | 206 | static void aeGetTime(long *seconds, long *milliseconds) 207 | { 208 | struct timeval tv; 209 | 210 | gettimeofday(&tv, NULL); 211 | *seconds = tv.tv_sec; 212 | *milliseconds = tv.tv_usec/1000; 213 | } 214 | 215 | static void aeAddMillisecondsToNow(es_int64_t milliseconds, long *sec, long *ms) { 216 | long cur_sec, cur_ms, when_sec, when_ms; 217 | 218 | aeGetTime(&cur_sec, &cur_ms); 219 | when_sec = (long)(cur_sec + milliseconds/1000); 220 | when_ms = (long)(cur_ms + milliseconds%1000); 221 | if (when_ms >= 1000) { 222 | when_sec ++; 223 | when_ms -= 1000; 224 | } 225 | *sec = when_sec; 226 | *ms = when_ms; 227 | } 228 | 229 | es_int64_t eco_poll_time_event_create(co_poll_t *poll, es_int64_t milliseconds, 230 | coTimeProc *proc, void *clientData, 231 | coEventFinalizerProc *finalizerProc) 232 | { 233 | es_int64_t id = poll->timeEventNextId++; 234 | coTimeEvent *te; 235 | 236 | te = malloc(sizeof(*te)); 237 | if (te == NULL) return ES_FAILURE; 238 | te->id = id; 239 | aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms); 240 | te->timeProc = proc; 241 | te->finalizerProc = finalizerProc; 242 | te->clientData = clientData; 243 | te->next = poll->timeEventHead; 244 | poll->timeEventHead = te; 245 | return id; 246 | } 247 | 248 | es_status_t eco_poll_time_event_delete(co_poll_t *poll, es_int64_t id) 249 | { 250 | coTimeEvent *te = poll->timeEventHead; 251 | while(te) { 252 | if (te->id == id) { 253 | te->id = ECO_POLL_DELETED_EVENT_ID; 254 | return ES_SUCCESS; 255 | } 256 | te = te->next; 257 | } 258 | return ES_FAILURE; /* NO event with the specified ID found */ 259 | } 260 | 261 | es_status_t eco_poll_time_fire_all(co_poll_t* poll) { 262 | long cur_sec, cur_ms; 263 | coTimeEvent *te; 264 | 265 | aeGetTime(&cur_sec, &cur_ms); 266 | 267 | te = poll->timeEventHead; 268 | while(te) { 269 | if (te->id != ECO_POLL_DELETED_EVENT_ID) { 270 | te->when_sec = cur_sec; 271 | te->when_ms = cur_ms; 272 | } 273 | te = te->next; 274 | } 275 | return ES_SUCCESS; 276 | } 277 | 278 | /* Search the first timer to fire. 279 | * This operation is useful to know how many time the select can be 280 | * put in sleep without to delay any event. 281 | * If there are no timers NULL is returned. 282 | * 283 | * Note that's O(N) since time events are unsorted. 284 | * Possible optimizations (not needed by Redis so far, but...): 285 | * 1) Insert the event in order, so that the nearest is just the head. 286 | * Much better but still insertion or deletion of timers is O(N). 287 | * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)). 288 | */ 289 | static coTimeEvent *aeSearchNearestTimer(co_poll_t *poll) 290 | { 291 | coTimeEvent *te = poll->timeEventHead; 292 | coTimeEvent *nearest = NULL; 293 | 294 | while(te) { 295 | if (!nearest || te->when_sec < nearest->when_sec || 296 | (te->when_sec == nearest->when_sec && 297 | te->when_ms < nearest->when_ms)) 298 | nearest = te; 299 | te = te->next; 300 | } 301 | return nearest; 302 | } 303 | 304 | /* Process time events */ 305 | static int processTimeEvents(co_poll_t *poll) { 306 | int processed = 0; 307 | coTimeEvent *te, *prev; 308 | es_int64_t maxId; 309 | time_t now = time(NULL); 310 | 311 | /* If the system clock is moved to the future, and then set back to the 312 | * right value, time events may be delayed in a random way. Often this 313 | * means that scheduled operations will not be performed soon enough. 314 | * 315 | * Here we try to detect system clock skews, and force all the time 316 | * events to be processed ASAP when this happens: the idea is that 317 | * processing events earlier is less dangerous than delaying them 318 | * indefinitely, and practice suggests it is. */ 319 | if (now < poll->lastTime) { 320 | te = poll->timeEventHead; 321 | while(te) { 322 | te->when_sec = 0; 323 | te = te->next; 324 | } 325 | } 326 | poll->lastTime = now; 327 | 328 | prev = NULL; 329 | te = poll->timeEventHead; 330 | maxId = poll->timeEventNextId-1; 331 | while(te) { 332 | long now_sec, now_ms; 333 | es_int64_t id; 334 | 335 | /* Remove events scheduled for deletion. */ 336 | if (te->id == ECO_POLL_DELETED_EVENT_ID) { 337 | coTimeEvent *next = te->next; 338 | if (prev == NULL) 339 | poll->timeEventHead = te->next; 340 | else 341 | prev->next = te->next; 342 | if (te->finalizerProc) 343 | te->finalizerProc(poll, te->clientData); 344 | free(te); 345 | te = next; 346 | continue; 347 | } 348 | 349 | /* Make sure we don't process time events created by time events in 350 | * this iteration. Note that this check is currently useless: we always 351 | * add new timers on the head, however if we change the implementation 352 | * detail, this check may be useful again: we keep it here for future 353 | * defense. */ 354 | if (te->id > maxId) { 355 | te = te->next; 356 | continue; 357 | } 358 | aeGetTime(&now_sec, &now_ms); 359 | if (now_sec > te->when_sec || 360 | (now_sec == te->when_sec && now_ms >= te->when_ms)) 361 | { 362 | int retval; 363 | 364 | id = te->id; 365 | retval = te->timeProc(poll, id, te->clientData); 366 | processed++; 367 | if (retval != ECO_POLL_NOMORE) { 368 | aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); 369 | } else { 370 | te->id = ECO_POLL_DELETED_EVENT_ID; 371 | } 372 | } 373 | prev = te; 374 | te = te->next; 375 | } 376 | return processed; 377 | } 378 | 379 | int eco_poll_process_events(co_poll_t *poll, int flags, es_int64_t timeout) 380 | { 381 | int processed = 0, numevents; 382 | 383 | /* Nothing to do? return ASAP */ 384 | if (!(flags & ECO_POLL_TIME_EVENTS) && !(flags & ECO_POLL_FILE_EVENTS)) return 0; 385 | 386 | /* Note that we want call select() even if there are no 387 | * file events to process as long as we want to process time 388 | * events, in order to sleep until the next time event is ready 389 | * to fire. */ 390 | if (poll->maxfd != -1 || 391 | ((flags & ECO_POLL_TIME_EVENTS) && !(timeout == 0))) { 392 | int j; 393 | coTimeEvent *shortest = NULL; 394 | struct timeval tv, *tvp; 395 | 396 | if ((flags & ECO_POLL_TIME_EVENTS) && !(timeout == 0)) 397 | shortest = aeSearchNearestTimer(poll); 398 | if (shortest) { 399 | long now_sec, now_ms; 400 | 401 | /* Calculate the time missing for the nearest 402 | * timer to fire. */ 403 | aeGetTime(&now_sec, &now_ms); 404 | tvp = &tv; 405 | tvp->tv_sec = shortest->when_sec - now_sec; 406 | if (shortest->when_ms < now_ms) { 407 | tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000; 408 | tvp->tv_sec --; 409 | } else { 410 | tvp->tv_usec = (shortest->when_ms - now_ms)*1000; 411 | } 412 | if (tvp->tv_sec < 0) tvp->tv_sec = 0; 413 | if (tvp->tv_usec < 0) tvp->tv_usec = 0; 414 | } else { 415 | if (timeout > 0) { 416 | tv.tv_sec = timeout / 1000; 417 | tv.tv_usec = (timeout % 1000) * 1000; 418 | tvp = &tv; 419 | } else if (timeout == 0) { 420 | /* If we have to check for events but need to return 421 | * ASAP because of timeout == 0 we need to set the timeout 422 | * to zero */ 423 | tv.tv_sec = tv.tv_usec = 0; 424 | tvp = &tv; 425 | } else { 426 | /* Otherwise we can block */ 427 | tvp = NULL; /* wait forever */ 428 | } 429 | } 430 | 431 | numevents = aeApiPoll(poll, tvp); 432 | for (j = 0; j < numevents; j++) { 433 | coFileEvent *fe = &poll->events[poll->fired[j].fd]; 434 | int mask = poll->fired[j].mask; 435 | int fd = poll->fired[j].fd; 436 | int rfired = 0; 437 | 438 | /* note the fe->mask & mask & ... code: maybe an already processed 439 | * event removed an element that fired and we still didn't 440 | * processed, so we check if the event is still valid. */ 441 | if (fe->mask & mask & ECO_POLL_READABLE) { 442 | rfired = 1; 443 | if (fe->rfileProc) fe->rfileProc(poll,fd,fe->clientData,mask); 444 | } 445 | if (fe->mask & mask & ECO_POLL_WRITABLE) { 446 | if (fe->wfileProc && (!rfired || fe->wfileProc != fe->rfileProc)) 447 | fe->wfileProc(poll,fd,fe->clientData,mask); 448 | } 449 | processed++; 450 | } 451 | } 452 | /* Check time events */ 453 | if (flags & ECO_POLL_TIME_EVENTS) 454 | processed += processTimeEvents(poll); 455 | 456 | return processed; /* return the number of processed file/time events */ 457 | } 458 | 459 | int eco_poll_wait(int fd, int mask, es_int64_t milliseconds) { 460 | #ifdef HAVE_POLL 461 | struct pollfd pfd; 462 | int retmask = 0, retval; 463 | 464 | memset(&pfd, 0, sizeof(pfd)); 465 | pfd.fd = fd; 466 | if (mask & ECO_POLL_READABLE) pfd.events |= POLLIN; 467 | if (mask & ECO_POLL_WRITABLE) pfd.events |= POLLOUT; 468 | 469 | if ((retval = poll(&pfd, 1, milliseconds)) == 1) { 470 | if (pfd.revents & POLLIN) 471 | retmask |= ECO_POLL_READABLE; 472 | if (pfd.revents & POLLOUT) 473 | retmask |= ECO_POLL_WRITABLE; 474 | if (pfd.revents & POLLERR) 475 | retmask |= ECO_POLL_WRITABLE; 476 | if (pfd.revents & POLLHUP) 477 | retmask |= ECO_POLL_WRITABLE; 478 | return retmask; 479 | } else { 480 | return retval; 481 | } 482 | #else //! 483 | fd_set rd, wr, ex; 484 | struct timeval t; 485 | 486 | t.tv_sec = milliseconds / 1000; 487 | t.tv_usec = (milliseconds % 1000) * 1000; 488 | 489 | FD_ZERO(&rd); 490 | FD_ZERO(&wr); 491 | FD_ZERO(&ex); 492 | 493 | if (mask & ECO_POLL_READABLE) { 494 | FD_SET(fd, &rd); 495 | } 496 | if (mask & ECO_POLL_WRITABLE) { 497 | FD_SET(fd, &wr); 498 | } 499 | 500 | errno = 0; 501 | return select(fd + 1, &rd, &wr, &ex, (milliseconds >= 0) ? &t : NULL); 502 | #endif 503 | } 504 | 505 | char *eco_poll_api_name(void) { 506 | return aeApiName(); 507 | } 508 | 509 | int eco_poll_getfd(co_poll_t* poll) { 510 | return aeApiHandle(poll); 511 | } 512 | -------------------------------------------------------------------------------- /src/EFiberScheduler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EFiberScheduler.cpp 3 | * 4 | * Created on: 2016-5-17 5 | * Author: cxxjava@163.com 6 | */ 7 | 8 | #include "./EContext.hh" 9 | #include "./EIoWaiter.hh" 10 | #include "./EFileContext.hh" 11 | #include "../inc/EFiberScheduler.hh" 12 | #include "../inc/EFiber.hh" 13 | #include "../inc/EFiberBlocker.hh" 14 | #include "../inc/EFiberDebugger.hh" 15 | 16 | #include 17 | 18 | namespace efc { 19 | namespace eco { 20 | 21 | //============================================================================= 22 | 23 | static long fdLimit(int deflim) { 24 | struct rlimit rlim; 25 | if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) 26 | return deflim; 27 | long limit = rlim.rlim_cur; 28 | return (limit < 0 ? deflim : rlim.rlim_cur); 29 | } 30 | 31 | //============================================================================= 32 | 33 | EThreadLocalStorage EFiberScheduler::currScheduler; 34 | EThreadLocalStorage EFiberScheduler::currIoWaiter; 35 | 36 | class SchedulerStub: public EObject { 37 | public: 38 | EFiberConcurrentQueue taskQueue; 39 | EIoWaiter ioWaiter; 40 | EIoWaiter* volatile hungIoWaiter; 41 | SchedulerStub(int maxEventSetSize) : 42 | ioWaiter(maxEventSetSize), hungIoWaiter(null) { 43 | } 44 | }; 45 | 46 | struct SchedulerLocal { 47 | EFiberScheduler* scheduler; 48 | EFiber* currFiber; 49 | 50 | SchedulerLocal(EFiberScheduler* fs): scheduler(fs), currFiber(null) {} 51 | }; 52 | 53 | class IoWaiterFiber: public EFiber { 54 | public: 55 | IoWaiterFiber(EIoWaiter* iw): ioWaiter(iw) { 56 | } 57 | virtual void run() { 58 | ioWaiter->loopProcessEvents(); 59 | } 60 | private: 61 | EIoWaiter* ioWaiter; 62 | }; 63 | 64 | class TimberFiber: public EFiber { 65 | private: 66 | sp timer; 67 | llong delay; 68 | llong period; 69 | public: 70 | TimberFiber(sp& t, llong d): timer(t), delay(d), period(0) { 71 | } 72 | TimberFiber(sp& t, llong d, llong p): timer(t), delay(d), period(p) { 73 | } 74 | void init() { 75 | timer->setFiber(this); 76 | } 77 | virtual void run() { 78 | EFiber::sleep(delay); 79 | if (period > 0) { 80 | while (!canceled) { 81 | EFiber::sleep(period); 82 | timer->run(); 83 | } 84 | } else { 85 | if (!canceled) timer->run(); 86 | } 87 | } 88 | }; 89 | 90 | class Timer: public EFiberTimer { 91 | public: 92 | Timer(std::function f): func(f) { 93 | } 94 | virtual void run() { 95 | func(); 96 | } 97 | private: 98 | std::function func; 99 | }; 100 | 101 | //============================================================================= 102 | 103 | EFiberScheduler::~EFiberScheduler() { 104 | delete schedulerStubs; 105 | delete hookedFiles; 106 | } 107 | 108 | EFiberScheduler::EFiberScheduler() : 109 | maxEventSetSize(fdLimit(FD_DEFAULT_CHUNKS * FD_CHUNK_CAPACITY)), 110 | threadNums(1), 111 | schedulerStubs(null), 112 | balanceCallback(null), 113 | scheduleCallback(null), 114 | hookedFiles(new EFileContextManager(maxEventSetSize)), 115 | interrupted(false) { 116 | // 117 | } 118 | 119 | EFiberScheduler::EFiberScheduler(int maxfd) : 120 | maxEventSetSize(ES_MAX(maxfd, fdLimit(FD_DEFAULT_CHUNKS * FD_CHUNK_CAPACITY))), 121 | threadNums(1), 122 | schedulerStubs(null), 123 | balanceCallback(null), 124 | scheduleCallback(null), 125 | hookedFiles(new EFileContextManager(maxEventSetSize)), 126 | interrupted(false) { 127 | // 128 | } 129 | 130 | void EFiberScheduler::scheduleIgnoreBalance(sp fiber, boolean ignoreBalance) { 131 | totalFiberCounter++; 132 | 133 | fiber->state = EFiber::RUNNABLE; 134 | fiber->setScheduler(this); 135 | 136 | if (!schedulerStubs) { 137 | defaultTaskQueue.add(new sp(fiber)); 138 | } else { 139 | int index = 0; 140 | if (ignoreBalance) { 141 | EFiber* activeFiber = EFiberScheduler::activeFiber(); 142 | if (activeFiber) { 143 | index = activeFiber->threadIndex; 144 | } 145 | } else { 146 | if (balanceCallback) { 147 | index = balanceCallback(fiber.get(), threadNums); 148 | } else { 149 | index = (balanceIndex++) % schedulerStubs->length(); 150 | } 151 | } 152 | SchedulerStub* ss = schedulerStubs->getAt(index); 153 | ss->taskQueue.add(new sp(fiber)); 154 | EIoWaiter* iw = ss->hungIoWaiter; 155 | if (iw) { 156 | iw->signal(); 157 | } 158 | } 159 | } 160 | 161 | void EFiberScheduler::schedule(sp fiber) { 162 | scheduleIgnoreBalance(fiber, false); 163 | } 164 | 165 | void EFiberScheduler::scheduleInheritThread(sp fiber) { 166 | scheduleIgnoreBalance(fiber, true); 167 | } 168 | 169 | #ifdef CPP11_SUPPORT 170 | sp EFiberScheduler::schedule(std::function f, int stackSize) { 171 | class Fiber: public EFiber { 172 | public: 173 | Fiber(std::function f, int stackSize): 174 | EFiber(stackSize), func(f) { 175 | } 176 | virtual void run() { 177 | func(); 178 | } 179 | private: 180 | std::function func; 181 | }; 182 | 183 | sp fiber(new Fiber(f, stackSize)); 184 | this->scheduleIgnoreBalance(fiber, false); //! 185 | return fiber; 186 | } 187 | sp EFiberScheduler::scheduleInheritThread(std::function f, int stackSize) { 188 | class Fiber: public EFiber { 189 | public: 190 | Fiber(std::function f, int stackSize): 191 | EFiber(stackSize), func(f) { 192 | } 193 | virtual void run() { 194 | func(); 195 | } 196 | private: 197 | std::function func; 198 | }; 199 | 200 | sp fiber(new Fiber(f, stackSize)); 201 | this->scheduleIgnoreBalance(fiber, true); //! 202 | return fiber; 203 | } 204 | #endif 205 | 206 | sp EFiberScheduler::addtimer(sp timer, llong delay) { 207 | if (delay > 0) { 208 | sp tf = new TimberFiber(timer, delay); 209 | tf->init(); 210 | this->schedule(tf); 211 | } 212 | return timer; 213 | } 214 | 215 | sp EFiberScheduler::addtimer(sp timer, EDate* time) { 216 | llong delay = time->getTime() - ESystem::currentTimeMillis(); 217 | if (delay > 0) { 218 | sp tf = new TimberFiber(timer, delay); 219 | tf->init(); 220 | this->schedule(tf); 221 | } 222 | return timer; 223 | } 224 | 225 | sp EFiberScheduler::addtimer(sp timer, llong delay, llong period) { 226 | if (delay > 0) { 227 | sp tf = new TimberFiber(timer, delay, period); 228 | tf->init(); 229 | this->schedule(tf); 230 | } 231 | return timer; 232 | } 233 | 234 | sp EFiberScheduler::addtimer(sp timer, EDate* firstTime, llong period) { 235 | llong delay = firstTime->getTime() - ESystem::currentTimeMillis(); 236 | if (delay > 0) { 237 | sp tf = new TimberFiber(timer, delay, period); 238 | tf->init(); 239 | this->schedule(tf); 240 | } 241 | return timer; 242 | } 243 | 244 | #ifdef CPP11_SUPPORT 245 | void EFiberScheduler::addtimer(std::function f, llong delay) { 246 | this->addtimer(new Timer(f), delay); 247 | } 248 | 249 | void EFiberScheduler::addtimer(std::function f, EDate* time) { 250 | this->addtimer(new Timer(f), time); 251 | } 252 | 253 | void EFiberScheduler::addtimer(std::function f, llong delay, llong period) { 254 | this->addtimer(new Timer(f), delay, period); 255 | } 256 | 257 | void EFiberScheduler::addtimer(std::function f, EDate* firstTime, llong period) { 258 | this->addtimer(new Timer(f), firstTime, period); 259 | } 260 | #endif 261 | 262 | #ifdef CPP11_SUPPORT 263 | void EFiberScheduler::setScheduleCallback(std::function callback) { 266 | this->scheduleCallback = callback; 267 | } 268 | #else 269 | void EFiberScheduler::setScheduleCallback(thread_schedule_callback_t* callback) { 270 | this->scheduleCallback = callback; 271 | } 272 | #endif 273 | 274 | #ifdef CPP11_SUPPORT 275 | void EFiberScheduler::setBalanceCallback(std::function balancer) { 276 | this->balanceCallback = balancer; 277 | } 278 | #else 279 | void EFiberScheduler::setBalanceCallback(fiber_schedule_balance_t* balancer) { 280 | this->balanceCallback = balancer; 281 | } 282 | #endif 283 | 284 | void EFiberScheduler::join() { 285 | EThread* currentThread = EThread::currentThread(); 286 | #ifdef CPP11_SUPPORT 287 | std::function callback = scheduleCallback; 290 | #else 291 | thread_schedule_callback_t* callback = scheduleCallback; 292 | #endif 293 | // create io waiter. 294 | long currentThreadID = currentThread->getId(); 295 | EIoWaiter ioWaiter(maxEventSetSize); 296 | SchedulerLocal schedulerLocal(this); 297 | 298 | currScheduler.set(&schedulerLocal); 299 | currIoWaiter.set(&ioWaiter); 300 | 301 | if (scheduleCallback) { 302 | scheduleCallback(0, SCHEDULE_BEFORE, currentThread, NULL); 303 | } 304 | 305 | for (;;) { 306 | if (interrupted) { 307 | goto CLEAN; 308 | } 309 | 310 | int total = totalFiberCounter.value(); 311 | 312 | sp* fiber_ = defaultTaskQueue.poll(); 313 | if (!fiber_) { 314 | if (total > 0) { 315 | /** 316 | * inactive fibers is BLOCKED or WAITING! 317 | */ 318 | int events = ioWaiter.onceProcessEvents(3000); 319 | 320 | if (scheduleCallback) { 321 | scheduleCallback(0, SCHEDULE_IDLE, currentThread, NULL); 322 | } 323 | 324 | continue; 325 | } else { 326 | break; 327 | } 328 | } 329 | EFiber* fiber = (*fiber_).get(); 330 | 331 | if (ioWaiter.getWaitersCount() > 0) { 332 | // io waiter process. 333 | int events = ioWaiter.onceProcessEvents(); 334 | ECO_DEBUG(EFiberDebugger::SCHEDULER, "return the number of events: %d", events); 335 | } 336 | 337 | if (!fiber->boundQueue) { 338 | fiber->boundQueue = &defaultTaskQueue; 339 | } 340 | fiber->boundThreadID = currentThreadID; 341 | 342 | // bind io waiter 343 | fiber->setIoWaiter(&ioWaiter); 344 | 345 | #ifdef DEBUG 346 | llong t1 = ESystem::nanoTime(); 347 | #endif 348 | 349 | schedulerLocal.currFiber = fiber; 350 | if (scheduleCallback) { 351 | scheduleCallback(0, FIBER_BEFORE, currentThread, fiber); 352 | } 353 | fiber->context->swapIn(); 354 | if (scheduleCallback) { 355 | scheduleCallback(0, FIBER_AFTER, currentThread, fiber); 356 | } 357 | schedulerLocal.currFiber = null; 358 | 359 | #ifdef DEBUG 360 | llong t2 = ESystem::nanoTime(); 361 | ECO_DEBUG(EFiberDebugger::SCHEDULER, "fiter run time: %lldns, %s", t2 - t1, currentThread->toString().c_str()); 362 | #endif 363 | 364 | switch (fiber->state) { 365 | case EFiber::RUNNABLE: 366 | { 367 | defaultTaskQueue.add(fiber_); 368 | } 369 | break; 370 | case EFiber::BLOCKED: 371 | { 372 | ES_ASSERT(fiber->blocker); 373 | if (!fiber->blocker->swapOut(fiber)) 374 | defaultTaskQueue.add(fiber_); 375 | } 376 | break; 377 | case EFiber::TERMINATED: 378 | delete fiber_; 379 | totalFiberCounter--; 380 | break; 381 | default: 382 | // 383 | break; 384 | } 385 | } 386 | 387 | CLEAN: 388 | currIoWaiter.set(null); 389 | currScheduler.set(null); 390 | 391 | // do some clean. 392 | clearFileContexts(); 393 | EContext::cleanOrignContext(); 394 | 395 | if (scheduleCallback) { 396 | scheduleCallback(0, SCHEDULE_AFTER, currentThread, NULL); 397 | } 398 | 399 | if (interrupted) { 400 | throw EInterruptedException(__FILE__, __LINE__); 401 | } 402 | } 403 | 404 | void EFiberScheduler::join(int threadNums) { 405 | 406 | if (threadNums < 1) { 407 | throw EIllegalArgumentException(__FILE__, __LINE__, "threadNums < 1"); 408 | } 409 | if (threadNums == 1) { 410 | join(); //! 411 | return; 412 | } 413 | // if (threadNums == 1) then ignore balanceCallback. 414 | this->threadNums = threadNums; 415 | 416 | // create thread local scheduler stub 417 | schedulerStubs = new EA(threadNums); 418 | for (int i=0; isetAt(i, new SchedulerStub(maxEventSetSize)); 420 | } 421 | 422 | // reset error. 423 | hasError.set(false); 424 | 425 | // multi-threading. 426 | class Worker: public EThread { 427 | public: 428 | EFiberScheduler* scheduler; 429 | int index; 430 | public: 431 | virtual void run() { 432 | try { 433 | scheduler->joinWithThreadBind(scheduler->schedulerStubs, index, this); 434 | } catch (...) { 435 | scheduler->hasError.set(true); 436 | } 437 | } 438 | }; 439 | 440 | /** 441 | * Don't forget current thread self to work together. 442 | */ 443 | 444 | // create other threads. 445 | EA > pool(threadNums - 1); 446 | for (int i=0; ischeduler = this; 449 | worker->index = i + 1; // 0 is for current thread. 450 | pool[i] = worker; 451 | } 452 | 453 | // dispatch fibers to each thread. 454 | sp* fiber_; 455 | while ((fiber_ = defaultTaskQueue.poll()) != null) { 456 | int i = 0; 457 | if (balanceCallback) { 458 | i = balanceCallback((*fiber_).get(), threadNums); 459 | } else { 460 | i = (balanceIndex++) % threadNums; 461 | } 462 | schedulerStubs->getAt(i)->taskQueue.add(fiber_); 463 | } 464 | 465 | // worker run. 466 | for (int i=0; istart(); 468 | } 469 | 470 | // current thread work. 471 | joinWithThreadBind(schedulerStubs, 0, EThread::currentThread()); 472 | 473 | // wait other threads work finished. 474 | for (int i=0; ijoin(); 476 | } 477 | 478 | // do some clean. 479 | clearFileContexts(); 480 | 481 | if (hasError.get()) { 482 | throw ERuntimeException(__FILE__, __LINE__, "join fail"); 483 | } 484 | } 485 | 486 | void EFiberScheduler::joinWithThreadBind(EA* schedulerStubs, 487 | int index, EThread* currentThread) { 488 | SchedulerStub* stub = schedulerStubs->getAt(index); 489 | EIoWaiter* ioWaiter = &stub->ioWaiter; 490 | EFiberConcurrentQueue* localQueue = &stub->taskQueue; 491 | 492 | long currentThreadID = currentThread->getId(); 493 | SchedulerLocal schedulerLocal(this); 494 | 495 | currScheduler.set(&schedulerLocal); 496 | currIoWaiter.set(ioWaiter); 497 | 498 | if (scheduleCallback) { 499 | scheduleCallback(index, SCHEDULE_BEFORE, currentThread, NULL); 500 | } 501 | 502 | for (;;) { 503 | if (interrupted) { 504 | goto CLEAN; 505 | } 506 | 507 | int total = totalFiberCounter.value(); 508 | 509 | // try get from thread local queue. 510 | sp* fiber_ = localQueue->poll(); 511 | if (!fiber_) { 512 | if (total > 0) { 513 | /** 514 | * inactive fibers is BLOCKED or WAITING! 515 | */ 516 | stub->hungIoWaiter = ioWaiter; 517 | int events = ioWaiter->onceProcessEvents(3000); 518 | stub->hungIoWaiter = null; 519 | 520 | if (scheduleCallback) { 521 | scheduleCallback(index, SCHEDULE_IDLE, currentThread, NULL); 522 | } 523 | 524 | continue; 525 | } else { 526 | break; 527 | } 528 | } 529 | EFiber* fiber = (*fiber_).get(); 530 | 531 | if (ioWaiter->getWaitersCount() > 0) { 532 | // io waiter process. 533 | int events = ioWaiter->onceProcessEvents(); 534 | ECO_DEBUG(EFiberDebugger::SCHEDULER, "return the number of events: %d", events); 535 | } 536 | 537 | fiber->setThreadIndex(index); 538 | 539 | if (!fiber->boundQueue) { 540 | fiber->boundQueue = localQueue; 541 | } 542 | fiber->boundThreadID = currentThreadID; 543 | 544 | // bind io waiter 545 | fiber->setIoWaiter(ioWaiter); 546 | 547 | #ifdef DEBUG 548 | llong t1 = ESystem::nanoTime(); 549 | #endif 550 | 551 | schedulerLocal.currFiber = fiber; 552 | if (scheduleCallback) { 553 | scheduleCallback(index, FIBER_BEFORE, currentThread, fiber); 554 | } 555 | fiber->context->swapIn(); 556 | if (scheduleCallback) { 557 | scheduleCallback(index, FIBER_AFTER, currentThread, fiber); 558 | } 559 | schedulerLocal.currFiber = null; 560 | 561 | #ifdef DEBUG 562 | llong t2 = ESystem::nanoTime(); 563 | ECO_DEBUG(EFiberDebugger::SCHEDULER, "fiter run time: %lldns, %s", t2 - t1, currentThread->toString().c_str()); 564 | #endif 565 | 566 | switch (fiber->state) { 567 | case EFiber::RUNNABLE: 568 | { 569 | // add to thread local queue. 570 | localQueue->add(fiber_); 571 | } 572 | break; 573 | case EFiber::BLOCKED: 574 | { 575 | ES_ASSERT(fiber->blocker); 576 | if (!fiber->blocker->swapOut(fiber)) { 577 | // add to thread local queue. 578 | localQueue->add(fiber_); 579 | } 580 | } 581 | break; 582 | case EFiber::TERMINATED: 583 | delete fiber_; 584 | totalFiberCounter--; 585 | break; 586 | default: 587 | // 588 | break; 589 | } 590 | } 591 | 592 | CLEAN: 593 | currIoWaiter.set(null); 594 | currScheduler.set(null); 595 | 596 | // do some clean. 597 | EContext::cleanOrignContext(); 598 | 599 | // try to notify another iowaiter in the same scheduler group. 600 | for (int i=0; ilength(); i++) { 601 | EIoWaiter* iw = &schedulerStubs->getAt(i)->ioWaiter; 602 | if (iw != ioWaiter) { 603 | iw->signal(); 604 | } 605 | } 606 | 607 | if (scheduleCallback) { 608 | scheduleCallback(index, SCHEDULE_AFTER, currentThread, NULL); 609 | } 610 | 611 | if (interrupted) { 612 | throw EInterruptedException(__FILE__, __LINE__); 613 | } 614 | } 615 | 616 | void EFiberScheduler::interrupt() { 617 | interrupted = true; 618 | 619 | //active all ioWatier. 620 | if (threadNums > 1 && schedulerStubs) { 621 | for (int i=0; ilength(); i++) { 622 | EIoWaiter& iw = schedulerStubs->getAt(i)->ioWaiter; 623 | iw.interrupt(); 624 | iw.signal(); 625 | } 626 | } else { 627 | EIoWaiter* iw = currentIoWaiter(); 628 | if (iw) { 629 | iw->interrupt(); 630 | iw->signal(); 631 | } 632 | } 633 | } 634 | 635 | boolean EFiberScheduler::isInterrupted() { 636 | return interrupted; 637 | } 638 | 639 | int EFiberScheduler::totalFiberCount() { 640 | return totalFiberCounter.value(); 641 | } 642 | 643 | EFiber* EFiberScheduler::activeFiber() { 644 | SchedulerLocal* sl = static_cast(currScheduler.get()); 645 | return sl ? sl->currFiber : null; 646 | } 647 | 648 | EFiberScheduler* EFiberScheduler::currentScheduler() { 649 | SchedulerLocal* sl = static_cast(currScheduler.get()); 650 | return sl ? sl->scheduler : null; 651 | } 652 | 653 | EIoWaiter* EFiberScheduler::currentIoWaiter() { 654 | return static_cast(currIoWaiter.get()); 655 | } 656 | 657 | sp EFiberScheduler::getFileContext(int fd) { 658 | return hookedFiles->get(fd); 659 | } 660 | 661 | void EFiberScheduler::delFileContext(int fd) { 662 | hookedFiles->remove(fd); 663 | } 664 | 665 | void EFiberScheduler::clearFileContexts() { 666 | hookedFiles->clear(); 667 | } 668 | 669 | } /* namespace eco */ 670 | } /* namespace efc */ 671 | -------------------------------------------------------------------------------- /test/testeco.cpp: -------------------------------------------------------------------------------- 1 | #include "es_main.h" 2 | #include "Eco.hh" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #ifdef __linux__ 16 | #include 17 | #include 18 | #include 19 | #endif 20 | #ifdef __APPLE__ 21 | #include 22 | #endif 23 | 24 | #ifdef CPP11_SUPPORT 25 | 26 | #define LOG(fmt,...) ESystem::out->printfln(fmt, ##__VA_ARGS__) 27 | 28 | static const char* g_ip = "0.0.0.0"; 29 | static const uint16_t g_port = 8888; 30 | 31 | static inline int do_read(int fd, char * b, int l) 32 | { 33 | int r = 0, t = 0; 34 | for (;;) { 35 | t = ::read(fd, b + r, l - r); 36 | if (t < 1) { 37 | // LOG("%s:%u, msg=%s", __FUNCTION__, __LINE__, strerror(errno)); 38 | return -1; 39 | } 40 | r += t; 41 | return r; 42 | } 43 | 44 | return -1; 45 | } 46 | 47 | class MySubFiber: public EFiber { 48 | public: 49 | virtual ~MySubFiber() { 50 | LOG("~MySubFiber()"); 51 | } 52 | MySubFiber() { 53 | } 54 | MySubFiber(int stackSize): EFiber(stackSize) { 55 | } 56 | virtual void run() { 57 | for (int i=0; i<20; i++) { 58 | LOG("MySubFiber run(%d)", i); 59 | 60 | errno = 5555; 61 | EFiber::yield(); 62 | LOG("errno5=%d", errno); 63 | } 64 | } 65 | }; 66 | 67 | class MyFiber: public EFiber { 68 | public: 69 | virtual ~MyFiber() { 70 | LOG("~MyFiber()"); 71 | } 72 | MyFiber() { 73 | } 74 | MyFiber(int stackSize): EFiber(stackSize) { 75 | } 76 | virtual void run() { 77 | LOG("fiber=%s", this->toString().c_str()); 78 | 79 | for (int i=0; i<200; i++) { 80 | LOG("MyFiber run(%d)", i); 81 | 82 | errno = 9999; 83 | EFiber::yield(); 84 | LOG("errno9=%d", errno); 85 | 86 | // throw EException(__FILE__, __LINE__, "e0"); 87 | 88 | try { 89 | throw EException(__FILE__, __LINE__, "e1"); 90 | } catch (EException& e) { 91 | EFiber::yield(); 92 | LOG("e1"); 93 | //// try { 94 | // throw EException(__FILE__, __LINE__, "e2"); 95 | //// } catch (...) { 96 | //// LOG("..."); 97 | //// } 98 | } 99 | } 100 | 101 | LOG("end of MyFiber::run()..."); 102 | } 103 | }; 104 | 105 | static void test_one_thread() { 106 | EFiberScheduler scheduler; 107 | scheduler.schedule(new MyFiber()); 108 | scheduler.schedule(new MyFiber()); 109 | scheduler.schedule(new MyFiber()); 110 | scheduler.schedule(new MyFiber()); 111 | scheduler.join(); 112 | 113 | LOG("end of test_one_thread()."); 114 | } 115 | 116 | static void test_multi_thread() { 117 | EFiberScheduler scheduler; 118 | 119 | for (int i=0; i<5; i++) { 120 | scheduler.schedule(new MyFiber()); 121 | 122 | scheduler.schedule([](){}); 123 | } 124 | 125 | scheduler.join(3); 126 | 127 | LOG("test_multi_thread() finished!"); 128 | } 129 | 130 | static void go_func_test1() { 131 | LOG("go_func_test1"); 132 | } 133 | 134 | static void go_func_test2(EFiberScheduler* scheduler) { 135 | LOG("go_func_test2, scheduler hashCode: %d", scheduler->hashCode()); 136 | } 137 | 138 | static void test_c11schedule() { 139 | EFiberScheduler scheduler; 140 | scheduler.schedule(new MyFiber()); 141 | scheduler.schedule(new MyFiber()); 142 | scheduler.schedule(new MyFiber()); 143 | scheduler.schedule(new MyFiber()); 144 | 145 | scheduler.schedule(go_func_test1); 146 | 147 | scheduler.schedule(std::bind(&go_func_test2, &scheduler)); 148 | 149 | scheduler.schedule( 150 | [](){ 151 | EFiber* fiber = EFiber::currentFiber(); 152 | LOG("fiber2=%s", fiber->toString().c_str()); 153 | 154 | for (int i=0; i<1000; i++) { 155 | errno = 1111; 156 | EFiber::yield(); 157 | LOG("i=%d, errno1=%d", i, errno); 158 | } 159 | } 160 | ); 161 | 162 | sp ths = EThread::executeX([&]() { 163 | EFiberScheduler scheduler; 164 | 165 | scheduler.schedule(new MyFiber()); 166 | 167 | scheduler.join(); 168 | }); 169 | 170 | // mast be before thread join(). 171 | scheduler.join(); 172 | 173 | ths->join(); 174 | 175 | LOG("fiber finished!"); 176 | } 177 | 178 | static void test_nesting() { 179 | EFiberScheduler scheduler; 180 | scheduler.schedule([&]() { 181 | char ss[100]; 182 | sprintf(ss, "lllllllllllllllllllllll"); 183 | LOG("parent fiber, s=%s", ss); 184 | 185 | EFiber* f = EFiber::currentFiber(); 186 | LOG("f->toString(): %s", f->toString().c_str()); 187 | 188 | for (int i=0; i<100; i++) { 189 | scheduler.schedule([&]() { 190 | // LOG("sub fiber, s=%s", ss); //error! ss out of scope. 191 | 192 | for (int i=0; i<100; i++) { 193 | EFiber* f = EFiber::currentFiber(); 194 | LOG("sf->toString(): %s", f->toString().c_str()); 195 | 196 | EFiber::yield(); 197 | } 198 | 199 | }, 400*1024); 200 | } 201 | }); 202 | scheduler.join(3); 203 | 204 | LOG("end of test_nesting()."); 205 | } 206 | 207 | static void test_channel() { 208 | EFiberChannel channel(0); 209 | EFiberScheduler scheduler; 210 | scheduler.schedule([&]() { 211 | for (int i=0; i<100; i++) { 212 | channel.write(new EString("channel data")); 213 | } 214 | }); 215 | scheduler.schedule([&]() { 216 | sp s; 217 | for (int i=0; i<200; i++) { 218 | if (channel.tryRead(s)) { 219 | LOG("recv s=%s", s->c_str()); 220 | } else { 221 | LOG("recv fail."); 222 | } 223 | } 224 | }); 225 | scheduler.join(); 226 | } 227 | 228 | static void test_channel_one_thread() { 229 | EFiberChannel channel(0); 230 | EFiberScheduler scheduler; 231 | scheduler.schedule([&]() { 232 | char ss[100]; 233 | sprintf(ss, "lllllllllllllllllllllll"); 234 | LOG("parent fiber, s=%s", ss); 235 | 236 | EFiber* f = EFiber::currentFiber(); 237 | LOG("f->toString(): %s", f->toString().c_str()); 238 | 239 | for (int i=0; i<100; i++) { 240 | scheduler.schedule([&]() { 241 | //LOG("sub fiber, s=%s", ss); //error! ss out of scope. 242 | 243 | for (int i=0; i<100; i++) { 244 | EFiber* f = EFiber::currentFiber(); 245 | LOG("sf->toString(): %s", f->toString().c_str()); 246 | 247 | channel.write(new EString("channel data")); 248 | 249 | // EFiber::yield(); 250 | 251 | LOG("after of write."); 252 | } 253 | 254 | }, 40*1024); 255 | } 256 | 257 | sp s; 258 | for (int i=0; i<100*100; i++) { 259 | s = channel.read(); 260 | LOG("recv s=%s", s->c_str()); 261 | } 262 | }); 263 | scheduler.join(); 264 | } 265 | 266 | #define RUNTIMES 100 267 | #define FLIP_READ_WRITE 0 268 | #define BOTH_IS_FIBER 0 269 | #define BOTH_IS_THREAD 1 270 | 271 | static void test_channel_multi_thread() { 272 | EFiberScheduler scheduler; 273 | 274 | // channel. 275 | EFiberChannel channel(1); 276 | 277 | scheduler.schedule([&]() { 278 | for (int i=0; i s = channel.read(); 285 | if (s != null) { 286 | LOG("recv s=%s", s->c_str()); 287 | } 288 | 289 | // LOG("after of read."); 290 | #else 291 | channel.write(new EString("channel data")); 292 | 293 | LOG("after of write."); 294 | #endif 295 | } 296 | 297 | // LOG("after of fiber."); 298 | }); 299 | } 300 | }); 301 | 302 | sp ths = EThread::executeX([&]() { 303 | #if BOTH_IS_FIBER 304 | EFiberScheduler scheduler; 305 | 306 | scheduler.schedule([&]() { 307 | for (int i=0; i s = channel.read(); 312 | if (s != null) { 313 | LOG("recv s=%s", s->c_str()); 314 | } 315 | #endif 316 | } 317 | }); 318 | 319 | scheduler.join(); 320 | #else 321 | #if FLIP_READ_WRITE 322 | for (int i=0; i s = channel.read(); 330 | if (s != null) { 331 | LOG("recv s=%s", s->c_str()); 332 | } 333 | 334 | LOG("after of read."); 335 | } 336 | #endif 337 | #endif 338 | 339 | #if BOTH_IS_THREAD 340 | for (int i=0; i ths2 = EThread::executeX([&]() { 349 | #if BOTH_IS_THREAD 350 | for (int i=0; i s = channel.read(); 352 | if (s != null) { 353 | // LOG("recv2 s=%s", s->c_str()); 354 | } 355 | } 356 | #endif 357 | }); 358 | 359 | // io wait 360 | scheduler.schedule([&]() { 361 | int i = 0; 362 | while (i++ < 50) { 363 | EFiber::sleep(5); 364 | LOG("sleep 3s."); 365 | } 366 | }); 367 | 368 | scheduler.join(4); 369 | ths->join(); 370 | ths2->join(); 371 | 372 | LOG("end of test_channel_multi_thread()."); 373 | } 374 | 375 | static void test_mutex() { 376 | EFiberMutex mutex; 377 | EFiberScheduler scheduler; 378 | 379 | int value = 0; 380 | 381 | mutex.lock(); 382 | 383 | scheduler.schedule([&]() { 384 | EFiber::sleep(1000); 385 | 386 | boolean r = mutex.tryLock(1, ETimeUnit::SECONDS); 387 | LOG("tryLock=%d...", r); 388 | if (r) { 389 | mutex.unlock(); 390 | } 391 | 392 | EFiber::sleep(3000); 393 | 394 | LOG("1..."); 395 | for (int i=0; i<3000; i++) { 396 | SYNCBLOCK(&mutex) { 397 | LOG("-- %d", value++); 398 | }} 399 | } 400 | }); 401 | 402 | // for (int i=0; i<100; i++) { 403 | scheduler.schedule([&]() { 404 | EFiber::sleep(3000); 405 | 406 | LOG("2..."); 407 | mutex.unlock(); 408 | LOG("3..."); 409 | 410 | EFiber::sleep(3000); 411 | 412 | for (int i=0; i<300; i++) { 413 | SYNCBLOCK(&mutex) { 414 | LOG("++ %d", value++); 415 | }} 416 | } 417 | }); 418 | // } 419 | scheduler.join(); 420 | 421 | LOG("end of test_mutex()."); 422 | } 423 | 424 | static void test_mutex_multi_thread() { 425 | EFiberMutex mutex; 426 | EFiberScheduler scheduler; 427 | 428 | int value = 0; 429 | 430 | mutex.lock(); 431 | 432 | #if 0 433 | scheduler.schedule([&]() { 434 | EFiber::sleep(1000); 435 | 436 | boolean r = mutex.tryLock(1, ETimeUnit::SECONDS); 437 | LOG("tryLock=%d...", r); 438 | if (r) { 439 | mutex.unlock(); 440 | } 441 | 442 | EFiber::sleep(3000); 443 | 444 | LOG("1..."); 445 | for (int i=0; i<300; i++) { 446 | SYNCBLOCK(&mutex) { 447 | LOG("-- %d", value++); 448 | }} 449 | } 450 | }); 451 | 452 | sp thread = EThread::executeX([&]() { 453 | EThread::sleep(2000); 454 | 455 | LOG("2..."); 456 | mutex.unlock(); 457 | LOG("3..."); 458 | 459 | EThread::sleep(3000); 460 | 461 | for (int i=0; i<300; i++) { 462 | SYNCBLOCK(&mutex) { 463 | LOG("++ %d", value++); 464 | }} 465 | } 466 | }); 467 | #else 468 | scheduler.schedule([&]() { 469 | EFiber::sleep(1000); 470 | 471 | boolean r = mutex.tryLock(1, ETimeUnit::SECONDS); 472 | LOG("tryLock1=%d...", r); 473 | if (r) { 474 | mutex.unlock(); 475 | } 476 | 477 | mutex.lock(); 478 | EFiber::sleep(2000); 479 | mutex.unlock(); 480 | 481 | LOG("1..."); 482 | for (int i=0; i<300; i++) { 483 | SYNCBLOCK(&mutex) { 484 | LOG("-- %d", value++); 485 | }} 486 | } 487 | }); 488 | 489 | sp thread = EThread::executeX([&]() { 490 | sleep(2); 491 | 492 | LOG("2..."); 493 | mutex.unlock(); 494 | LOG("3..."); 495 | 496 | sleep(1); 497 | 498 | boolean r = mutex.tryLock(1, ETimeUnit::SECONDS); 499 | LOG("tryLock2=%d...", r); 500 | if (r) { 501 | mutex.unlock(); 502 | } 503 | 504 | for (int i=0; i<300; i++) { 505 | SYNCBLOCK(&mutex) { 506 | LOG("++ %d", value++); 507 | }} 508 | } 509 | }); 510 | #endif 511 | 512 | scheduler.join(); 513 | thread->join(); 514 | 515 | LOG("end of test_mutex_multi_thread()."); 516 | } 517 | 518 | static void test_condition() { 519 | EFiberScheduler scheduler; 520 | EFiberCondition condition; 521 | 522 | for (int i=0; i<10; i++) { 523 | scheduler.schedule([&]() { 524 | LOG("await, i=%d", i); 525 | condition.await(); 526 | LOG("wakeup, i=%d", i); 527 | }); 528 | } 529 | 530 | #if 1 531 | for (int i=0; i<10; i++) { 532 | scheduler.schedule([&]() { 533 | EFiber::sleep(5000); 534 | condition.signal(); 535 | }); 536 | } 537 | #else 538 | scheduler.schedule([&]() { 539 | EFiber::sleep(5000); 540 | condition.signalAll(); 541 | }); 542 | #endif 543 | 544 | scheduler.join(); 545 | 546 | LOG("end of test_condition()."); 547 | } 548 | 549 | static void test_sleep() { 550 | EFiberScheduler scheduler; 551 | 552 | scheduler.schedule([]() { 553 | LOG("sleep 10 second."); 554 | try { 555 | EFiber::sleep(10000); 556 | } catch (EInterruptedException& e) { 557 | e.printStackTrace(); 558 | } 559 | }); 560 | 561 | scheduler.schedule([&scheduler]() { 562 | LOG("sleep 1 second."); 563 | EFiber::sleep(1000); 564 | scheduler.interrupt(); 565 | }); 566 | 567 | scheduler.join(4); 568 | 569 | LOG("end of test_sleep()."); 570 | } 571 | 572 | static void fiber_destroyed_callback(void* data) { 573 | if (data) { 574 | EString* s = (EString*)data; 575 | delete s; 576 | } 577 | } 578 | 579 | static void test_local() { 580 | EFiberScheduler scheduler; 581 | EFiberLocal localValue; 582 | EFiberLocal localValue2(fiber_destroyed_callback); 583 | 584 | for (int i=0; i<100; i++) { 585 | scheduler.schedule([&, i]() { 586 | EString x = EString::formatOf("fiber %d data", i); 587 | LOG("x=%s", x.c_str()); 588 | localValue.set(new EString(x)); 589 | EString* s = localValue.get(); 590 | LOG("s=%s", s->c_str()); 591 | delete localValue.remove(); 592 | 593 | localValue2.set(new EString("freed at call back.")); 594 | LOG("s=%s", localValue2.get()->c_str()); 595 | }); 596 | } 597 | 598 | scheduler.join(); 599 | 600 | LOG("end of test_local()."); 601 | } 602 | 603 | static void test_hook_connect1() { 604 | EFiberScheduler scheduler; 605 | 606 | scheduler.schedule([]() { 607 | int fd = socket(AF_INET, SOCK_STREAM, 0); 608 | 609 | sockaddr_in addr; 610 | memset(&addr, 0, sizeof(addr)); 611 | addr.sin_family = AF_INET; 612 | addr.sin_port = htons(8096); 613 | addr.sin_addr.s_addr = inet_addr("61.135.169.121"); 614 | if (-1 == connect(fd, (sockaddr*)&addr, sizeof(addr))) { 615 | LOG("connect error:%s\n", strerror(errno)); 616 | return; 617 | } else { 618 | LOG("connect success."); 619 | } 620 | 621 | close(fd); 622 | }); 623 | 624 | scheduler.join(); 625 | } 626 | 627 | static void test_hook_connect2() { 628 | EFiberScheduler scheduler; 629 | 630 | for (int i=0; i<100; i++) { 631 | scheduler.schedule([]() { 632 | int fd = socket(AF_INET, SOCK_STREAM, 0); 633 | 634 | sockaddr_in addr; 635 | memset(&addr, 0, sizeof(addr)); 636 | addr.sin_family = AF_INET; 637 | addr.sin_port = htons(80); 638 | addr.sin_addr.s_addr = inet_addr("61.135.169.121"); 639 | if (-1 == connect(fd, (sockaddr*)&addr, sizeof(addr))) { 640 | LOG("connect error:%s\n", strerror(errno)); 641 | return; 642 | } else { 643 | LOG("connect success."); 644 | } 645 | 646 | close(fd); 647 | }); 648 | } 649 | 650 | scheduler.join(3); 651 | } 652 | 653 | static void test_hook_poll() { 654 | EFiberScheduler scheduler; 655 | 656 | scheduler.schedule([]() { 657 | int fd = eso_net_socket(AF_INET, SOCK_STREAM, 0); 658 | int result = eso_net_connect(fd, "61.135.169.121", 80, 50); //poll in eso_net_connect(). 659 | LOG("result=%d", result); 660 | eso_net_close(fd); 661 | }); 662 | 663 | scheduler.join(); 664 | } 665 | 666 | #ifdef TEST_HOOK_SELECT 667 | static void test_hook_select() { 668 | EFiberScheduler scheduler; 669 | 670 | scheduler.schedule([]() { 671 | int fd = eso_net_socket(AF_INET, SOCK_STREAM, 0); 672 | int result = eso_net_connect(fd, "61.135.169.121", 80, 50); 673 | LOG("connect result=%d", result); 674 | 675 | eso_net_write(fd, "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/html\r\n\r\nHello,world", 75); 676 | 677 | fd_set readset, writeset, exceptset; 678 | FD_ZERO(&readset); 679 | FD_ZERO(&writeset); 680 | FD_ZERO(&exceptset); 681 | FD_SET(fd, &readset); 682 | FD_SET(fd, &writeset); 683 | FD_SET(fd, &exceptset); 684 | struct timeval tv = {5, 0}; 685 | // result = select(fd+1, &readset, &writeset, &exceptset, &tv); 686 | result = select(fd+1, &readset, NULL, NULL, &tv); 687 | LOG("select result=%d, errno=%d", result, errno); 688 | if (FD_ISSET(fd, &readset)) { 689 | char buf[1024]; 690 | result = eso_net_read(fd, buf, sizeof(buf)); 691 | LOG("read result=%d, s=%s", result, buf); 692 | } 693 | if (FD_ISSET(fd, &writeset)) { 694 | LOG("select result=%d, can write", result); 695 | } 696 | 697 | eso_net_close(fd); 698 | }); 699 | 700 | scheduler.join(); 701 | } 702 | #endif 703 | 704 | static void test_hook_sleep() { 705 | EFiberScheduler scheduler; 706 | 707 | scheduler.schedule([]() { 708 | while (1) { 709 | sleep(10); 710 | LOG("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); 711 | } 712 | }); 713 | 714 | for (int i=0; i<10; i++) { 715 | scheduler.schedule([]() { 716 | while (1) { 717 | LOG("z"); 718 | EFiber::yield(); 719 | } 720 | }); 721 | } 722 | 723 | scheduler.join(); 724 | } 725 | 726 | static void sigfunc(int sig_no) { 727 | LOG("signaled."); 728 | int temp = 1000; 729 | while (temp-- > 0) 730 | ; 731 | } 732 | 733 | static void test_hook_signal() { 734 | EFiberScheduler scheduler; 735 | 736 | signal(SIGINT, sigfunc); 737 | 738 | scheduler.schedule([]() { 739 | while (1) { 740 | sleep(10); 741 | LOG("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); 742 | } 743 | }); 744 | 745 | scheduler.join(); 746 | } 747 | 748 | static void test_hook_fcntl() { 749 | struct flock _lock; 750 | _lock.l_type = F_WRLCK; 751 | _lock.l_whence = SEEK_SET; 752 | _lock.l_start = 0; 753 | _lock.l_len = 0; 754 | int fd = open( "/tmp/t",O_CREAT|O_RDWR,S_IRWXU|S_IRGRP|S_IWGRP|S_IRWXO ); 755 | int ret = fcntl( fd,F_SETLK,&_lock ); 756 | LOG("ret=%d", ret); 757 | ret = fcntl(fd, F_GETLK,&_lock ); 758 | LOG("ret=%d", ret); 759 | close(fd); 760 | 761 | EFiberScheduler scheduler; 762 | scheduler.schedule([&]() { 763 | struct flock _lock; 764 | _lock.l_type = F_WRLCK; 765 | _lock.l_whence = SEEK_SET; 766 | _lock.l_start = 0; 767 | _lock.l_len = 0; 768 | int fd = open( "/tmp/t2",O_CREAT|O_RDWR,S_IRWXU|S_IRGRP|S_IWGRP|S_IRWXO ); 769 | int ret = fcntl( fd,F_SETLK,&_lock ); 770 | LOG("ret=%d", ret); 771 | ret = fcntl(fd, F_GETLK,&_lock ); 772 | LOG("ret=%d", ret); 773 | close(fd); 774 | }); 775 | scheduler.join(); 776 | } 777 | 778 | static void test_hook_nonblocking() { 779 | int flags; 780 | int fd = socket(AF_INET, SOCK_STREAM, 0); 781 | 782 | flags = fcntl(fd, F_GETFL, 0); 783 | if (flags & O_NONBLOCK) { 784 | LOG("O_NONBLOCK"); 785 | } 786 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); 787 | 788 | flags = fcntl(fd, F_GETFL, 0); 789 | if (flags & O_NONBLOCK) { 790 | LOG("O_NONBLOCK"); 791 | } 792 | fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 793 | 794 | flags = fcntl(fd, F_GETFL, 0); 795 | if (flags & O_NONBLOCK) { 796 | LOG("O_NONBLOCK"); 797 | } 798 | 799 | EFiberScheduler scheduler; 800 | scheduler.schedule([&]() { 801 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); 802 | 803 | flags = fcntl(fd, F_GETFL, 0); 804 | if (flags & O_NONBLOCK) { 805 | LOG("O_NONBLOCK"); 806 | } 807 | 808 | fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 809 | int on = 1; ioctl(fd, FIONBIO, &on); 810 | 811 | //==================== 812 | 813 | sockaddr_in addr; 814 | memset(&addr, 0, sizeof(addr)); 815 | addr.sin_family = AF_INET; 816 | addr.sin_port = htons(80); 817 | addr.sin_addr.s_addr = inet_addr("61.135.169.121"); 818 | if (-1 == connect(fd, (sockaddr*)&addr, sizeof(addr))) { 819 | LOG("connect error:%s\n", strerror(errno)); 820 | return; 821 | } else { 822 | LOG("connect success."); 823 | } 824 | 825 | flags = fcntl(fd, F_GETFL, 0); 826 | if (flags & O_NONBLOCK) { 827 | LOG("O_NONBLOCK"); 828 | } 829 | 830 | ENetWrapper::configureBlocking(fd, false); 831 | 832 | //close(fd); 833 | }); 834 | scheduler.join(); 835 | 836 | flags = ::fcntl(fd, F_GETFL); 837 | LOG("flags=%d", flags); 838 | if (flags & O_NONBLOCK) { 839 | LOG("O_NONBLOCK"); 840 | } 841 | 842 | close(fd); 843 | } 844 | 845 | static void test_hook_read_write() { 846 | // signal(SIGINT, sigfunc); 847 | 848 | #define MULTITHREAD 1 849 | 850 | EFiberScheduler scheduler; 851 | #if MULTITHREAD 852 | for (int i=0; i<100; i++) { 853 | #endif 854 | scheduler.schedule([&]() { 855 | int fd = socket(AF_INET, SOCK_STREAM, 0); 856 | 857 | int timeout = 10000; 858 | struct timeval tv; 859 | tv.tv_sec = timeout / 1000; 860 | tv.tv_usec = (timeout % 1000) * 1000; 861 | ENetWrapper::setOption(fd, ESocketOptions::_SO_TIMEOUT, &tv, sizeof(tv)); 862 | 863 | sockaddr_in addr; 864 | memset(&addr, 0, sizeof(addr)); 865 | addr.sin_family = AF_INET; 866 | addr.sin_port = htons(8096); 867 | addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 868 | if (-1 == connect(fd, (sockaddr*)&addr, sizeof(addr))) { 869 | LOG("connect error:%s\n", strerror(errno)); 870 | return; 871 | } else { 872 | LOG("connect success."); 873 | } 874 | 875 | const char* req = "GET / HTTP/1.1\r\nHost: localhost:8096\r\n\r\n"; 876 | int written = write(fd, req, strlen(req)); 877 | if (written != strlen(req)) { 878 | LOG("write error."); 879 | } 880 | char buf[152] = {0}; 881 | int readed = read(fd, buf, sizeof(buf)); 882 | LOG("readed=%d, errno=%d, buf=%s", readed, errno, buf); 883 | 884 | // write big data. 885 | es_data_t* wbuf = eso_mmalloc(102400000); 886 | eso_mmemfill(wbuf, '1'); 887 | written = write(fd, (char *)wbuf, eso_mnode_size(wbuf)); 888 | eso_mfree(wbuf); 889 | 890 | close(fd); 891 | }); 892 | #if MULTITHREAD 893 | } 894 | scheduler.join(3); 895 | #else 896 | scheduler.join(); 897 | #endif 898 | } 899 | 900 | static void test_hook_pipe() { 901 | es_pipe_t* pipe = eso_pipe_create(); 902 | 903 | EFiberScheduler scheduler; 904 | scheduler.schedule([&]() { 905 | 906 | void* buf[32]; 907 | int r = eso_fread(buf, sizeof(buf), pipe->in); 908 | LOG("r=%d, s=%s", r, buf); 909 | 910 | }); 911 | scheduler.schedule([&]() { 912 | sleep(3); 913 | eso_fwrite("123456", 6, pipe->out); 914 | LOG("w"); 915 | }); 916 | scheduler.join(); 917 | 918 | eso_pipe_destroy(&pipe); 919 | } 920 | 921 | static void test_timer() { 922 | class Timer1: public EFiberTimer { 923 | public: 924 | virtual void run() { 925 | LOG("timer1 run..."); 926 | } 927 | }; 928 | 929 | class Timer2: public EFiberTimer { 930 | public: 931 | virtual void run() { 932 | LOG("timer2 run..."); 933 | } 934 | }; 935 | 936 | EFiberScheduler scheduler; 937 | 938 | sp t1 = scheduler.addtimer(new Timer1(), 3000, 1000); 939 | sp t2 = scheduler.addtimer(new Timer2(), 1000, 1000); 940 | 941 | scheduler.schedule([&]() { 942 | EFiber::sleep(10000); 943 | t2->cancel(); 944 | EFiber::sleep(10000); 945 | t1->cancel(); 946 | }); 947 | 948 | scheduler.addtimer([]() { 949 | LOG("www"); 950 | }, 1, 2000); 951 | 952 | scheduler.join(); 953 | } 954 | 955 | static void test_hook_kqueue() { 956 | EFiberScheduler scheduler; 957 | scheduler.schedule([&]() { 958 | 959 | #ifdef __APPLE__ 960 | int fd = socket(AF_INET, SOCK_STREAM, 0); 961 | 962 | sockaddr_in addr; 963 | memset(&addr, 0, sizeof(addr)); 964 | addr.sin_family = AF_INET; 965 | addr.sin_port = htons(8096); 966 | addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 967 | if (-1 == connect(fd, (sockaddr*)&addr, sizeof(addr))) { 968 | LOG("connect error:%s\n", strerror(errno)); 969 | return; 970 | } else { 971 | LOG("connect success."); 972 | } 973 | 974 | const char* req = "GET / HTTP/1.1\r\nHost: localhost:8096\r\n\r\n"; 975 | int written = write(fd, req, strlen(req)); 976 | if (written != strlen(req)) { 977 | LOG("write error."); 978 | } 979 | 980 | ENetWrapper::configureBlocking(fd, false); 981 | 982 | int kq = kqueue(); 983 | 984 | struct timespec tv = {3, 0}; 985 | struct kevent ke; 986 | struct kevent ke_; 987 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 988 | int n = kevent(kq, &ke, 1, &ke_, 1, &tv); 989 | if (n > 0) { 990 | char buf[152] = {0}; 991 | int readed = read(fd, buf, sizeof(buf)); 992 | LOG("readed=%d, errno=%d, buf=%s", readed, errno, buf); 993 | } 994 | 995 | close(fd); 996 | close(kq); 997 | #endif 998 | }); 999 | 1000 | scheduler.join(); 1001 | } 1002 | 1003 | static void test_hook_gethostbyname() { 1004 | EFiberScheduler scheduler; 1005 | 1006 | scheduler.schedule([&]() { 1007 | while (true) { 1008 | LOG("xxx"); 1009 | const char* name = "x2.xxx.xxx"; 1010 | struct hostent *hp = gethostbyname(name); 1011 | if (hp) { 1012 | struct in_addr **addrp = (struct in_addr **) hp->h_addr_list; 1013 | EInetAddress ia(name, (*addrp)->s_addr); 1014 | LOG("xxx %s", ia.toString().c_str()); 1015 | } 1016 | //EFiber::yield(); 1017 | } 1018 | }); 1019 | 1020 | scheduler.schedule([&]() { 1021 | while (true) { 1022 | EFiber::sleep(100); 1023 | LOG("yyy"); 1024 | } 1025 | }); 1026 | 1027 | scheduler.join(); 1028 | } 1029 | 1030 | static void test_hook_sendfile() { 1031 | EFiberScheduler scheduler; 1032 | scheduler.schedule([&](){ 1033 | int ret; 1034 | int accept_fd = socket(AF_INET, SOCK_STREAM, 0); 1035 | ES_ASSERT(accept_fd >= 0); 1036 | 1037 | #ifdef __APPLE__ 1038 | int v = 1; 1039 | ret = setsockopt(accept_fd, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)); 1040 | #else 1041 | #if LINUX_VERSION_CODE>= KERNEL_VERSION(3,9,0) 1042 | int v = 1; 1043 | ret = setsockopt(accept_fd, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v)); 1044 | #endif 1045 | #endif 1046 | 1047 | sockaddr_in addr; 1048 | addr.sin_family = AF_INET; 1049 | addr.sin_port = htons(g_port); 1050 | addr.sin_addr.s_addr = inet_addr(g_ip); 1051 | ret = bind(accept_fd, (sockaddr*)&addr, sizeof(addr)); 1052 | ES_ASSERT(ret == 0); 1053 | 1054 | ret = listen(accept_fd, 8192); 1055 | ES_ASSERT(ret == 0); 1056 | for (;;) { 1057 | socklen_t addr_len = sizeof(addr); 1058 | int s = accept(accept_fd, (sockaddr*)&addr, &addr_len); 1059 | if (s < 0) { 1060 | perror("accept error:"); 1061 | continue; 1062 | } 1063 | 1064 | int size = 256 * 1024; 1065 | setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof(size)); 1066 | 1067 | int flag = 1; 1068 | setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); 1069 | 1070 | struct linger li = { 1, 0 }; 1071 | setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li)); 1072 | 1073 | scheduler.schedule([s]() { 1074 | while (1) { 1075 | int rsize = 1024; 1076 | char rbuf[rsize]; 1077 | 1078 | ssize_t rn = do_read(s, rbuf, rsize); 1079 | if (rn < 0) { 1080 | shutdown(s, 0x02); 1081 | close(s); 1082 | break; 1083 | } 1084 | 1085 | const char* filename = "xxx.zip"; 1086 | struct stat filestat; 1087 | stat(filename, &filestat); 1088 | int fd = open(filename, O_RDONLY); 1089 | off_t off = 0; 1090 | off_t num = filestat.st_size; 1091 | #ifdef __APPLE__ 1092 | int result = sendfile(fd, s, 0, &num, NULL, 0); 1093 | #else 1094 | int result = sendfile(s, fd, &off, num); 1095 | #endif 1096 | close(fd); 1097 | printf("reuslt=%d, fd=%d, num=%d, errno=%d, error=%s\n", result, fd, num, errno, strerror(errno)); 1098 | } 1099 | }); 1100 | } 1101 | }); 1102 | scheduler.join(); 1103 | } 1104 | 1105 | static void test_not_hook_file() { 1106 | EFiberScheduler scheduler; 1107 | scheduler.schedule([&]() { 1108 | es_file_t* pfile = eso_fopen("./test_file.txt", "r+"); 1109 | // int fd = ::open("./test_file.txt", O_RDWR); 1110 | int r = ::write(eso_fileno(pfile), (void*)"1234567890", 10); 1111 | eso_fclose(pfile); 1112 | // ::close(fd); 1113 | }); 1114 | scheduler.join(); 1115 | } 1116 | 1117 | static void test_nio() { 1118 | sp socketChannel = ESocketChannel::open(); 1119 | socketChannel->configureBlocking(false ); 1120 | ESelector* selector = ESelector::open(); 1121 | socketChannel->register_(selector, ESelectionKey::OP_CONNECT); 1122 | EInetSocketAddress SERVER_ADDRESS("localhost", 8096); 1123 | // EInetSocketAddress SERVER_ADDRESS("10.211.55.8", 8899); 1124 | socketChannel->connect(&SERVER_ADDRESS); 1125 | 1126 | ESet >* selectionKeys; 1127 | sp > > iterator; 1128 | sp selectionKey; 1129 | sp client; 1130 | int count = 0; 1131 | EIOByteBuffer sendbuffer(512); 1132 | EIOByteBuffer receivebuffer(512); 1133 | 1134 | int nn = 0; 1135 | do { 1136 | nn++; 1137 | 1138 | selector->select(); 1139 | selectionKeys = selector->selectedKeys(); 1140 | iterator = selectionKeys->iterator(); 1141 | while (iterator->hasNext()) { 1142 | selectionKey = iterator->next(); 1143 | if (selectionKey->isConnectable()) { 1144 | LOG("client connect"); 1145 | client = dynamic_pointer_cast(selectionKey->channel()); 1146 | if (client->isConnectionPending()) { 1147 | client->finishConnect(); 1148 | LOG("connect finished!"); 1149 | sendbuffer.clear(); 1150 | const char* req = "GET / HTTP/1.1\r\nHost: localhost:8096\r\n\r\n"; 1151 | sendbuffer.put(req, strlen(req)); 1152 | sendbuffer.flip(); 1153 | client->write(&sendbuffer); 1154 | } 1155 | client->register_(selector, ESelectionKey::OP_READ); 1156 | } else if (selectionKey->isReadable()) { 1157 | client = dynamic_pointer_cast(selectionKey->channel()); 1158 | receivebuffer.clear(); 1159 | try { 1160 | count = client->read(&receivebuffer); 1161 | } catch (...) { 1162 | client->close(); 1163 | } 1164 | if(count>0){ 1165 | receivebuffer.flip(); 1166 | LOG("recev server:%s", receivebuffer.current()); 1167 | } 1168 | selectionKey->cancel(); 1169 | } 1170 | } 1171 | selectionKeys->clear(); 1172 | } while (nn < 2); 1173 | 1174 | selector->close(); 1175 | delete selector; 1176 | 1177 | socketChannel->close(); 1178 | } 1179 | 1180 | static void test_sslsocket() { 1181 | char buffer[4096]; 1182 | int ret; 1183 | ESSLSocket *socket = new ESSLSocket(); 1184 | socket->setSSLParameters( 1185 | "./certs/client/client-cert.pem", 1186 | "./certs/client/client-key.pem", 1187 | null); 1188 | socket->setReceiveBufferSize(10240); 1189 | socket->connect("localhost", 8443, 3000); 1190 | socket->setSoTimeout(3000); 1191 | char *get_str = "GET / HTTP/1.1\r\n" 1192 | "Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/xaml+xml, application/x-ms-xbap, application/x-ms-application, */*\r\n" 1193 | "Accept-Language: zh-cn\r\n" 1194 | "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727)\r\n" 1195 | "Accept-Encoding: gzip, deflate\r\n" 1196 | "Host: www.baidu.com\r\n" 1197 | "Connection: Close\r\n" //"Connection: Keep-Alive\r\n" 1198 | "Cookie: BAIDUID=72CBD0B204EC83BF3C5C0FA7A9C89637:FG=1\r\n\r\n"; 1199 | EOutputStream *sos = socket->getOutputStream(); 1200 | EInputStream *sis = socket->getInputStream(); 1201 | sos->write(get_str, strlen(get_str)); 1202 | LOG("socket available=[%d]", sis->available()); 1203 | try { 1204 | while ((ret = sis->read(buffer, sizeof(buffer))) > 0) { 1205 | LOG("socket ret=[%d], available=[%d]", ret, sis->available()); 1206 | LOG("socket read=[%s]", buffer); 1207 | } 1208 | } catch (...) { 1209 | } 1210 | sis->close(); 1211 | sos->close(); 1212 | socket->close(); 1213 | delete socket; 1214 | } 1215 | 1216 | static void test_sslserversocket() { 1217 | ESSLServerSocket *serverSocket = new ESSLServerSocket(); 1218 | serverSocket->setSSLParameters( 1219 | "./certs/server/server-cert.pem", 1220 | "./certs/server/server-key.pem", 1221 | null, "./certs/ca/ca-cert.pem"); 1222 | serverSocket->setReuseAddress(true); 1223 | serverSocket->bind(8443); 1224 | LOG("serverSocket=%s", serverSocket->toString().c_str()); 1225 | int count = 0; 1226 | char buffer[11]; 1227 | while (count < 10) { 1228 | try { 1229 | ESSLSocket *clientSocket = serverSocket->accept(); 1230 | if (!clientSocket) continue; 1231 | count++; 1232 | EInetSocketAddress *isar = clientSocket->getRemoteSocketAddress(); 1233 | EInetSocketAddress *isal = clientSocket->getLocalSocketAddress(); 1234 | LOG("socket rip=[%s], rport=%d", isar->getHostName(), isar->getPort()); 1235 | LOG("socket lip=[%s], lport=%d", isal->getHostName(), isal->getPort()); 1236 | try { 1237 | EInputStream *sis = clientSocket->getInputStream(); 1238 | eso_memset(buffer, 0, sizeof(buffer) - 1); 1239 | sis->read(buffer, sizeof(buffer)); 1240 | LOG("socket read=[%s]", buffer); 1241 | 1242 | EOutputStream *sos = clientSocket->getOutputStream(); 1243 | sos->write("HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/html\r\n\r\nHello,world"); 1244 | } catch (EIOException &e) { 1245 | LOG("read e=%s", e.toString().c_str()); 1246 | } 1247 | delete clientSocket; 1248 | } catch (...) { 1249 | LOG("accept error."); 1250 | } 1251 | } 1252 | delete serverSocket; 1253 | } 1254 | 1255 | static void test_efc_in_fiber() { 1256 | EFiberScheduler scheduler; 1257 | 1258 | scheduler.schedule([&]() { 1259 | while (true) { 1260 | // test_nio(); 1261 | // test_sslsocket(); 1262 | test_sslserversocket(); 1263 | } 1264 | }); 1265 | 1266 | scheduler.schedule([&]() { 1267 | while (true) { 1268 | sleep(1); 1269 | LOG("xxx"); 1270 | EFiber::yield(); 1271 | } 1272 | }); 1273 | 1274 | scheduler.schedule([&]() { 1275 | while (true) { 1276 | sleep(5); 1277 | LOG("yyy"); 1278 | EFiber::yield(); 1279 | } 1280 | }); 1281 | 1282 | scheduler.join(); 1283 | } 1284 | 1285 | static void test_balance() { 1286 | EFiberScheduler scheduler; 1287 | 1288 | scheduler.schedule([&]() { 1289 | EFiber* fiber = EFiber::currentFiber(); 1290 | LOG("childFiber0 index = %d", fiber->getThreadIndex()); 1291 | 1292 | sp childFiber1 = new EFiberTarget([&]() { 1293 | EFiber* fiber = EFiber::currentFiber(); 1294 | while (true) { 1295 | sleep(1); 1296 | LOG("childFiber3 index = %d", fiber->getThreadIndex()); 1297 | } 1298 | }); 1299 | childFiber1->setTag(1); 1300 | scheduler.schedule(childFiber1); 1301 | 1302 | sp childFiber2 = new EFiberTarget([]() { 1303 | EFiber* fiber = EFiber::currentFiber(); 1304 | while (true) { 1305 | sleep(1); 1306 | LOG("childFiber4 index = %d", fiber->getThreadIndex()); 1307 | } 1308 | }); 1309 | childFiber2->setTag(2); 1310 | scheduler.scheduleInheritThread(childFiber2); 1311 | }); 1312 | 1313 | scheduler.schedule([&]() { 1314 | EFiber* fiber = EFiber::currentFiber(); 1315 | while (true) { 1316 | sleep(1); 1317 | LOG("childFiber1 index = %d", fiber->getThreadIndex()); 1318 | EFiber::yield(); 1319 | } 1320 | }); 1321 | 1322 | scheduler.schedule([&]() { 1323 | EFiber* fiber = EFiber::currentFiber(); 1324 | while (true) { 1325 | sleep(5); 1326 | LOG("childFiber2 index = %d", fiber->getThreadIndex()); 1327 | EFiber::yield(); 1328 | } 1329 | }); 1330 | 1331 | // scheduler.setBalanceCallback([](EFiber* fiber, int threadNums) { 1332 | // long tag = fiber->getTag(); 1333 | // LOG("fiber tag = %ld", tag); 1334 | // if (tag == 1) { 1335 | // return 1; 1336 | // } 1337 | // if (tag == 2) { 1338 | // return 2; 1339 | // } 1340 | // return 0; 1341 | // }); 1342 | 1343 | scheduler.setScheduleCallback([](int threadIndex, 1344 | EFiberScheduler::SchedulePhase schedulePhase, EThread* currentThread, 1345 | EFiber* currentFiber) { 1346 | switch (schedulePhase) { 1347 | case EFiberScheduler::SCHEDULE_BEFORE: { 1348 | if (currentFiber) { 1349 | LOG("currentFiber index = %d", currentFiber->getThreadIndex()); 1350 | } 1351 | break; 1352 | } 1353 | } 1354 | }); 1355 | 1356 | scheduler.join(4); 1357 | } 1358 | 1359 | static void test_hook_dso() { 1360 | EFiberScheduler scheduler; 1361 | 1362 | scheduler.schedule([&]() { 1363 | EFiber* fiber = EFiber::currentFiber(); 1364 | typedef void (libtest_func)(); //from libtest.dylib 1365 | es_dso_t *dso = eso_dso_load("../eco/test/libtest.so"); 1366 | libtest_func* libtest = (libtest_func*)eso_dso_sym(dso, "libtest"); 1367 | for (int i=0; i<10; i++) { 1368 | libtest(); //calls puts() from libSystem.B.dylib 1369 | } 1370 | eso_dso_unload(&dso); 1371 | }); 1372 | 1373 | scheduler.schedule([&]() { 1374 | EFiber* fiber = EFiber::currentFiber(); 1375 | while (true) { 1376 | sleep(1); 1377 | LOG("childFiber index = %d", fiber->getThreadIndex()); 1378 | } 1379 | }); 1380 | 1381 | scheduler.join(); 1382 | } 1383 | 1384 | MAIN_IMPL(testeco) { 1385 | printf("main()\n"); 1386 | 1387 | ESystem::init(argc, argv); 1388 | 1389 | printf("inited.\n"); 1390 | 1391 | int i = 0; 1392 | try { 1393 | boolean loop = EBoolean::parseBoolean(ESystem::getProgramArgument("loop")); 1394 | 1395 | // EFiberDebugger::getInstance().debugOn(EFiberDebugger::SCHEDULER); 1396 | 1397 | do { 1398 | // test_one_thread(); 1399 | // test_multi_thread(); 1400 | // test_c11schedule(); 1401 | // test_nesting(); 1402 | // test_channel(); 1403 | // test_channel_one_thread(); 1404 | // test_channel_multi_thread(); 1405 | // test_mutex(); 1406 | // test_mutex_multi_thread(); 1407 | // test_condition(); 1408 | // test_sleep(); 1409 | // test_timer(); 1410 | // test_local(); 1411 | // test_hook_connect1(); 1412 | // test_hook_connect2(); 1413 | // test_hook_poll(); 1414 | // test_hook_select(); 1415 | // test_hook_sleep(); 1416 | // test_hook_signal(); //todo: 1417 | // test_hook_fcntl(); 1418 | // test_hook_nonblocking(); 1419 | // test_hook_read_write(); 1420 | // test_hook_pipe(); 1421 | // test_hook_kqueue(); 1422 | // test_hook_gethostbyname(); 1423 | // test_hook_sendfile(); 1424 | // test_not_hook_file(); 1425 | // test_nio(); 1426 | // test_sslsocket(); 1427 | // test_efc_in_fiber(); 1428 | // test_balance(); 1429 | test_hook_dso(); 1430 | 1431 | // } while (++i < 5); 1432 | } while (0); 1433 | } 1434 | catch (EException& e) { 1435 | e.printStackTrace(); 1436 | } 1437 | catch (...) { 1438 | printf("catch all...\n"); 1439 | } 1440 | 1441 | printf("exit...\n"); 1442 | 1443 | ESystem::exit(0); 1444 | 1445 | return 0; 1446 | } 1447 | 1448 | #endif //!CPP11_SUPPORT 1449 | --------------------------------------------------------------------------------