[問題] 關於 llvm 的 ThreadSafeModule 的疑惑

看板C_and_CPP (C/C++)作者 (靜夜)時間5年前 (2019/04/13 16:16), 5年前編輯推噓0(0067)
留言67則, 2人參與, 5年前最新討論串1/1
最近在用 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

04/14 00:46, 5年前 , 1F
你就要看呼叫 getContextLock() 的地方了, 他比較像
04/14 00:46, 1F

04/14 00:47, 5年前 , 2F
"Module with a mutex" 的概念, 不過他都 move 給你
04/14 00:47, 2F

04/14 00:50, 5年前 , 3F
註冊的 NotifyCompiledFunction, 等於生殺大權都交給
04/14 00:50, 3F

04/14 00:50, 5年前 , 4F
你, 其實就等於已經 release. 第 2 個是語言的問題
04/14 00:50, 4F

04/14 00:54, 5年前 , 5F
, 為了傳遞和 this 相同的 const 語意, 簡單說如果透
04/14 00:54, 5F

04/14 00:54, 5年前 , 6F
過 const ThreadSafeModule& 拿到的 Module 物件也必
04/14 00:54, 6F

04/14 00:56, 5年前 , 7F
須是 const 的理由是: const T& 在你 statement 結束
04/14 00:56, 7F

04/14 00:57, 5年前 , 8F
前保證的語意是 "物件一直都存在", 所以當 this 給你
04/14 00:57, 8F

04/14 00:57, 5年前 , 9F
保證但回傳的 Module 不保證是很奇怪的 (因為 owning
04/14 00:57, 9F

04/14 00:58, 5年前 , 10F
語意), 另一方面 this 不保證但 Module 保證也很奇怪
04/14 00:58, 10F

04/14 00:59, 5年前 , 11F
這表示即使它給你 Module, 它還是可以偷偷把 Module
04/14 00:59, 11F

04/14 00:59, 5年前 , 12F
給 delete 掉
04/14 00:59, 12F
謝謝回覆,問題1的話生殺大權是拿回來了,不過變成另一種東西...有一種被強迫的感覺 問題2呢,想一想的確從 const 物件拿出來的東西,既然交給它管了那也該是 const。不 過「const T& 在你 statement 結束前保證的語意是 "物件一直都存在"」有點難懂,是 說 reference 到一個 const T,這個 reference 一直都有效的意思? ※ 編輯: Lipraxde (140.113.56.71), 04/14/2019 11:48:26

04/14 16:21, 5年前 , 13F
是啦, 不過既然你能把它的資源 move 到別邊就不是什
04/14 16:21, 13F

04/14 16:23, 5年前 , 14F
麼大問題. 問題 2 涉及的不只是 constness 的語意,
04/14 16:23, 14F

04/14 16:24, 5年前 , 15F
而且還包含 lifetime 的保證, 你可以看這個範例
04/14 16:24, 15F

04/14 16:26, 5年前 , 16F
https://bit.ly/2VL8RY3 也就是說一旦你打算回傳/接
04/14 16:26, 16F

04/14 16:28, 5年前 , 17F
收到一個 const T&, 你需要保證/可以假設它在某段區
04/14 16:28, 17F

04/14 16:29, 5年前 , 18F
間內都是活著的(意即可以透過這個 ref 去存取它)
04/14 16:29, 18F

04/14 16:57, 5年前 , 19F
所以你可以想像如果兩種版本的 getModule() 回傳值和
04/14 16:57, 19F

04/14 16:58, 5年前 , 20F
this 不同調的衝突在哪裡 (用多個 thread 的角度來看
04/14 16:58, 20F

04/14 16:59, 5年前 , 21F
) 至於為什麼要有兩個版本? 其實就是想兩種不同語意
04/14 16:59, 21F

04/14 16:59, 5年前 , 22F
下都能叫用, 只是能提供的保證不同
04/14 16:59, 22F
例子我懂了, const T& 延長了那個暫時物件的 lifetime,保證它一定存在 不過它這裡是 getModule 是傳回 pointer 耶,用 pointer 會有保證的效果嗎? 多個 thread 的情況,要獲得保證一定要先呼叫 getContextLock 才行吧? ※ 編輯: Lipraxde (140.113.56.71), 04/14/2019 20:29:06

04/14 21:08, 5年前 , 23F
對指標 de-ref 也是一樣的, 除了原本 lifetime 的保
04/14 21:08, 23F

04/14 21:09, 5年前 , 24F
證, 另外還有 nullable 的語意, 算是不好的介面設計
04/14 21:09, 24F

04/14 21:09, 5年前 , 25F
多個 thread 的情況下, 簡單舉個例子: 對於同樣的int
04/14 21:09, 25F

04/14 21:10, 5年前 , 26F
物件, thread a 拿到的如果是 int*, thread b 拿到的
04/14 21:10, 26F

04/14 21:11, 5年前 , 27F
是 const int&, 那麼這邊就有一個隱含的語意: 在 b
04/14 21:11, 27F

04/14 21:11, 5年前 , 28F
結束存取之前, a 都不可以把這個物件給 delete 掉
04/14 21:11, 28F

04/14 21:14, 5年前 , 29F
不過這算是和 execution model 相關的討論, 簡單的作
04/14 21:14, 29F

04/14 21:14, 5年前 , 30F
法就是 read/write 都用鎖, 存取前確認合法性, 這樣
04/14 21:14, 30F

04/14 21:15, 5年前 , 31F
就能確保正確性
04/14 21:15, 31F
假如是用 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

04/15 00:09, 5年前 , 32F
bind to temporary 是一回事, 而延長 lifetime 就是
04/15 00:09, 32F

04/15 00:10, 5年前 , 33F
為了達成我所說的保證, 也就是 const T& 背後帶來的
04/15 00:10, 33F

04/15 00:10, 5年前 , 34F
目的
04/15 00:10, 34F

04/15 00:11, 5年前 , 35F
不只是 "有/無 const" 這樣表面上的問題
04/15 00:11, 35F

04/15 00:42, 5年前 , 36F
int& 算是 caller/callee 之間需要協議好這個物件的
04/15 00:42, 36F

04/15 00:43, 5年前 , 37F
生命週期是如何, 也許到 callee 的某個時間點就無法
04/15 00:43, 37F

04/15 00:44, 5年前 , 38F
de-ref, 而 const int& 給的語意更為強烈, 也就是在
04/15 00:44, 38F

04/15 00:45, 5年前 , 39F
callee 呼叫結束前物件都是可用的, 除了無法透過 ref
04/15 00:45, 39F

04/15 00:45, 5年前 , 40F
去 modify, 其實 caller 還有一個責任是 callee 觀察
04/15 00:45, 40F

04/15 00:46, 5年前 , 41F
到的物件行為到呼叫結束前必須不變
04/15 00:46, 41F
就我對 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

04/15 02:58, 5年前 , 42F
在函式中, 我們把 callee 以及 caller 角色分開,
04/15 02:58, 42F

04/15 02:59, 5年前 , 43F
callee 加上 const 修飾, 即是和 caller 要求對應的
04/15 02:59, 43F

04/15 03:00, 5年前 , 44F
保證, 當然 caller 可以不理會, 譬如接收 const Foo&
04/15 03:00, 44F

04/15 03:01, 5年前 , 45F
然後進行兩次 foo.getXXX() 呼叫, 這時候 callee 預
04/15 03:01, 45F

04/15 03:02, 5年前 , 46F
期不管呼叫再多次得到的值都要是相同的. 這就是加上
04/15 03:02, 46F

04/15 03:02, 5年前 , 47F
const 會需要 caller 做出的保證, 所以不單單只是
04/15 03:02, 47F

04/15 03:02, 5年前 , 48F
callee 不想改它的值這麼簡單
04/15 03:02, 48F

04/15 03:05, 5年前 , 49F
所以簡單說 const 不只是描述 "存取方式", 也描述了
04/15 03:05, 49F

04/15 03:05, 5年前 , 50F
"存取的一致性", 當然也包含 lifetime
04/15 03:05, 50F

04/15 03:13, 5年前 , 51F
那回來看 getModule() 回傳 const Module* 即是它給
04/15 03:13, 51F

04/15 03:13, 5年前 , 52F
出的保證: 在 this 為 const ThreadSafeModule* 的情
04/15 03:13, 52F

04/15 03:13, 5年前 , 53F
況下 (const member function), 取得的 Module 物件
04/15 03:13, 53F

04/15 03:13, 5年前 , 54F
行為是不變的 (包含 lifetime)
04/15 03:13, 54F

04/15 03:33, 5年前 , 55F
caller 沒保證一致性的例子 https://bit.ly/2IlHpgj
04/15 03:33, 55F

04/15 03:40, 5年前 , 56F
接收 const Foo&,呼叫 foo.getXXX()多次,預期拿到相
04/15 03:40, 56F

04/15 03:40, 5年前 , 57F
同的物件,由於這個物件屬於 foo,所以也要是 const 物
04/15 03:40, 57F

04/15 03:40, 5年前 , 58F
件,不然 foo 就失去 const 的性質了
04/15 03:40, 58F

04/15 03:53, 5年前 , 59F
這樣說得算是完整了嗎?
04/15 03:53, 59F

04/15 03:58, 5年前 , 60F
對喔, 這是因為 owning 語意的關係, 如果這個物件不
04/15 03:58, 60F

04/15 03:59, 5年前 , 61F
屬於 foo, 那就沒必要維持 const 語意
04/15 03:59, 61F

04/15 04:04, 5年前 , 62F
既然 callee 要求對應的保證,那 Compiler 可以優化它
04/15 04:04, 62F

04/15 04:04, 5年前 , 63F
?可是加了優化後得到的輸出還是有看到 i 被改成 1。Co
04/15 04:04, 63F

04/15 04:04, 5年前 , 64F
mpiler 聰明的判斷出這個 caller 沒做出相應的保證,抑
04/15 04:04, 64F

04/15 04:04, 5年前 , 65F
或是這個保證只是給 programmer 看的?
04/15 04:04, 65F

04/15 04:14, 5年前 , 66F
因為是參考(指標也是)所以能做的優化有限, 但這個也
04/15 04:14, 66F

04/15 04:15, 5年前 , 67F
是 programmer 需要遵守, 屬於語意層面的規範
04/15 04:15, 67F
感謝您花這麼多時間回覆,看來是因為 C 沒有 owning 語意,我又忽略掉 const[A 還有 "存取的一致性" 這層意義,才會出現問題2。我應該把 C++ 當門新的語言學才是 ※ 編輯: Lipraxde (140.113.56.71), 04/15/2019 20:59:08
文章代碼(AID): #1SiPhf4O (C_and_CPP)
文章代碼(AID): #1SiPhf4O (C_and_CPP)