看板 C_and_CPP 關於我們 聯絡資訊
最近在用 llvm 的 OrcJIT,要將 Module 丟進去跑之前要轉成一個叫 ThreadSafeModule 的東東,再用 llvm::orc::IRLayer add 進去 ThreadSafeModule 大概長這樣: class ThreadSafeModule { public: ThreadSafeModule() = default; ThreadSafeModule(ThreadSafeModule &&Other) = default; ThreadSafeModule &operator=(ThreadSafeModule &&Other); ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx) : M(std::move(M)), TSCtx(std::move(Ctx)) {} ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx) : M(std::move(M)), TSCtx(std::move(TSCtx)) {} Module *getModule() { return M.get(); } const Module *getModule() const { return M.get(); } ThreadSafeContext::Lock getContextLock() { return TSCtx.getLock(); } explicit operator bool(); private: std::unique_ptr<Module> M; ThreadSafeContext TSCtx; }; OrcJIT 跑完後如果中間有設 setNotifyCompiled 就會把 ThreadSafeModule 丟回來 然後 Module 就變成 ThreadSafeModule 把 ThreadSafeModule 釋放掉連帶著 Module 也釋放掉了 問題(Question): 1. 這個 ThreadSafeModule 為什麼要弄得像黑洞一樣?不把 Module release 出來 2. 它的 member function 'getModule',為什麼要寫一個有 const 一個沒有兩個版本? -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 140.113.210.55 ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1555143401.A.118.html
loveme00835: 你就要看呼叫 getContextLock() 的地方了, 他比較像 04/14 00:46
loveme00835: "Module with a mutex" 的概念, 不過他都 move 給你 04/14 00:47
loveme00835: 註冊的 NotifyCompiledFunction, 等於生殺大權都交給 04/14 00:50
loveme00835: 你, 其實就等於已經 release. 第 2 個是語言的問題 04/14 00:50
loveme00835: , 為了傳遞和 this 相同的 const 語意, 簡單說如果透 04/14 00:54
loveme00835: 過 const ThreadSafeModule& 拿到的 Module 物件也必 04/14 00:54
loveme00835: 須是 const 的理由是: const T& 在你 statement 結束 04/14 00:56
loveme00835: 前保證的語意是 "物件一直都存在", 所以當 this 給你 04/14 00:57
loveme00835: 保證但回傳的 Module 不保證是很奇怪的 (因為 owning 04/14 00:57
loveme00835: 語意), 另一方面 this 不保證但 Module 保證也很奇怪 04/14 00:58
loveme00835: 這表示即使它給你 Module, 它還是可以偷偷把 Module 04/14 00:59
loveme00835: 給 delete 掉 04/14 00:59
謝謝回覆,問題1的話生殺大權是拿回來了,不過變成另一種東西...有一種被強迫的感覺 問題2呢,想一想的確從 const 物件拿出來的東西,既然交給它管了那也該是 const。不 過「const T& 在你 statement 結束前保證的語意是 "物件一直都存在"」有點難懂,是 說 reference 到一個 const T,這個 reference 一直都有效的意思? ※ 編輯: Lipraxde (140.113.56.71), 04/14/2019 11:48:26
loveme00835: 是啦, 不過既然你能把它的資源 move 到別邊就不是什 04/14 16:21
loveme00835: 麼大問題. 問題 2 涉及的不只是 constness 的語意, 04/14 16:23
loveme00835: 而且還包含 lifetime 的保證, 你可以看這個範例 04/14 16:24
loveme00835: https://bit.ly/2VL8RY3 也就是說一旦你打算回傳/接 04/14 16:26
loveme00835: 收到一個 const T&, 你需要保證/可以假設它在某段區 04/14 16:28
loveme00835: 間內都是活著的(意即可以透過這個 ref 去存取它) 04/14 16:29
loveme00835: 所以你可以想像如果兩種版本的 getModule() 回傳值和 04/14 16:57
loveme00835: this 不同調的衝突在哪裡 (用多個 thread 的角度來看 04/14 16:58
loveme00835: ) 至於為什麼要有兩個版本? 其實就是想兩種不同語意 04/14 16:59
loveme00835: 下都能叫用, 只是能提供的保證不同 04/14 16:59
例子我懂了, const T& 延長了那個暫時物件的 lifetime,保證它一定存在 不過它這裡是 getModule 是傳回 pointer 耶,用 pointer 會有保證的效果嗎? 多個 thread 的情況,要獲得保證一定要先呼叫 getContextLock 才行吧? ※ 編輯: Lipraxde (140.113.56.71), 04/14/2019 20:29:06
loveme00835: 對指標 de-ref 也是一樣的, 除了原本 lifetime 的保 04/14 21:08
loveme00835: 證, 另外還有 nullable 的語意, 算是不好的介面設計 04/14 21:09
loveme00835: 多個 thread 的情況下, 簡單舉個例子: 對於同樣的int 04/14 21:09
loveme00835: 物件, thread a 拿到的如果是 int*, thread b 拿到的 04/14 21:10
loveme00835: 是 const int&, 那麼這邊就有一個隱含的語意: 在 b 04/14 21:11
loveme00835: 結束存取之前, a 都不可以把這個物件給 delete 掉 04/14 21:11
loveme00835: 不過這算是和 execution model 相關的討論, 簡單的作 04/14 21:14
loveme00835: 法就是 read/write 都用鎖, 存取前確認合法性, 這樣 04/14 21:14
loveme00835: 就能確保正確性 04/14 21:15
假如是用 int& 就沒有這個保證了嗎? 我看了 Lifetime of temporaries bound to references,裡面寫到: For any statement explicitly binding a reference to a temporary, the lifetime of all temporaries in the statement are extended to match the lifetime of the reference. 似乎跟有沒有 const 修飾沒有關係呀,還是說這個是在講不同的事? ※ 編輯: Lipraxde (140.113.56.71), 04/15/2019 00:03:32
loveme00835: bind to temporary 是一回事, 而延長 lifetime 就是 04/15 00:09
loveme00835: 為了達成我所說的保證, 也就是 const T& 背後帶來的 04/15 00:10
loveme00835: 目的 04/15 00:10
loveme00835: 不只是 "有/無 const" 這樣表面上的問題 04/15 00:11
loveme00835: int& 算是 caller/callee 之間需要協議好這個物件的 04/15 00:42
loveme00835: 生命週期是如何, 也許到 callee 的某個時間點就無法 04/15 00:43
loveme00835: de-ref, 而 const int& 給的語意更為強烈, 也就是在 04/15 00:44
loveme00835: callee 呼叫結束前物件都是可用的, 除了無法透過 ref 04/15 00:45
loveme00835: 去 modify, 其實 caller 還有一個責任是 callee 觀察 04/15 00:45
loveme00835: 到的物件行為到呼叫結束前必須不變 04/15 00:46
就我對 reference 的理解來說: int& // 參考到某個 int const int& // 參考到某個 const int 考慮到 lifetime 的保證,那就是: int& // 參考到某個 int,這個參考可能在某個時間點後就不能相信它了 const int& // 參考到某個 const int,由於目標是個 const,不會變,所以【可以/要】 // 保證這個參考有效 是說這個 const 用來修飾 int,為什麼要去影響 reference 的有效性? 可以保證有效,但是一定要做出這個保證? ※ 編輯: Lipraxde (140.113.56.71), 04/15/2019 02:13:02
loveme00835: 在函式中, 我們把 callee 以及 caller 角色分開, 04/15 02:58
loveme00835: callee 加上 const 修飾, 即是和 caller 要求對應的 04/15 02:59
loveme00835: 保證, 當然 caller 可以不理會, 譬如接收 const Foo& 04/15 03:00
loveme00835: 然後進行兩次 foo.getXXX() 呼叫, 這時候 callee 預 04/15 03:01
loveme00835: 期不管呼叫再多次得到的值都要是相同的. 這就是加上 04/15 03:02
loveme00835: const 會需要 caller 做出的保證, 所以不單單只是 04/15 03:02
loveme00835: callee 不想改它的值這麼簡單 04/15 03:02
loveme00835: 所以簡單說 const 不只是描述 "存取方式", 也描述了 04/15 03:05
loveme00835: "存取的一致性", 當然也包含 lifetime 04/15 03:05
loveme00835: 那回來看 getModule() 回傳 const Module* 即是它給 04/15 03:13
loveme00835: 出的保證: 在 this 為 const ThreadSafeModule* 的情 04/15 03:13
loveme00835: 況下 (const member function), 取得的 Module 物件 04/15 03:13
loveme00835: 行為是不變的 (包含 lifetime) 04/15 03:13
loveme00835: caller 沒保證一致性的例子 https://bit.ly/2IlHpgj 04/15 03:33
Lipraxde: 接收 const Foo&,呼叫 foo.getXXX()多次,預期拿到相 04/15 03:40
Lipraxde: 同的物件,由於這個物件屬於 foo,所以也要是 const 物 04/15 03:40
Lipraxde: 件,不然 foo 就失去 const 的性質了 04/15 03:40
Lipraxde: 這樣說得算是完整了嗎? 04/15 03:53
loveme00835: 對喔, 這是因為 owning 語意的關係, 如果這個物件不 04/15 03:58
loveme00835: 屬於 foo, 那就沒必要維持 const 語意 04/15 03:59
Lipraxde: 既然 callee 要求對應的保證,那 Compiler 可以優化它 04/15 04:04
Lipraxde: ?可是加了優化後得到的輸出還是有看到 i 被改成 1。Co 04/15 04:04
Lipraxde: mpiler 聰明的判斷出這個 caller 沒做出相應的保證,抑 04/15 04:04
Lipraxde: 或是這個保證只是給 programmer 看的? 04/15 04:04
loveme00835: 因為是參考(指標也是)所以能做的優化有限, 但這個也 04/15 04:14
loveme00835: 是 programmer 需要遵守, 屬於語意層面的規範 04/15 04:15
感謝您花這麼多時間回覆,看來是因為 C 沒有 owning 語意,我又忽略掉 const[A 還有 "存取的一致性" 這層意義,才會出現問題2。我應該把 C++ 當門新的語言學才是 ※ 編輯: Lipraxde (140.113.56.71), 04/15/2019 20:59:08