In Go, goroutines are meant to explicitly signal each other; further, it 
seems very intentional that goroutines are never interrupted in the middle 
of execution like an interrupt service routine might in a lower-level 
situation.

It was mentioned in a prior message a bit, but to add more details: I think 
I would solve this problem using channels and a select statement rather 
than busy waiting (e.g. looping on TryLock). Specifically, if you use a 
buffered channel of capacity 1 as a mutual exclusion guarantee (send to 
channel "locks" it, receive "unlocks" it), then you can use another channel 
as the "interrupt delivery" mechanism. Using a select statement allows you 
to simultaneously attempt locking and interrupt triaging. Of course this 
requires your goroutines to explicitly check for interrupts from time to 
time, but as I mentioned, I expect this is an intentional design choice of 
the Go programming language and I doubt things will change.

Note that this passing around of a channel (in the form of a 
context.Context: https://pkg.go.dev/context) is the "right" way to listen 
for timeout signals in Go too. Timeouts are sometimes implemented as some 
sort of interrupt in other languages, but the preference for Go is to 
explicitly check for that signal. This drastically simplifies the 
programming experience because avoids the need to reason about your 
goroutine arbitrarily executing different code in the middle of some other 
statement.

On Thursday, March 17, 2022 at 3:14:51 PM UTC-6 mumbling...@gmail.com wrote:

> Thanks! It's nice to have it natively I suppose. Still doesn't completely 
> solve my issue, but at least I won't have to implement custom lock 
> primitives.
>
> On Thursday, March 17, 2022 at 1:47:47 PM UTC+1 peterGo wrote:
>
>> Go1.18 has a new Mutex TryLock method.   
>> https://pkg.go.dev/sy...@go1.18#Mutex.TryLock 
>> <https://pkg.go.dev/sync@go1.18#Mutex.TryLock>
>>
>> Petr
>>
>> On Wednesday, March 16, 2022 at 9:36:50 PM UTC-4 mumbling...@gmail.com 
>> wrote:
>>
>>> I'm currently working on a project where I emulate a RISC-V processor 
>>> for the purpose of using Go when teaching concepts of operating systems.
>>> I'd like for the emulator to resemble a "real" processor to the point 
>>> that a realistic operating system can be implemented with most of the 
>>> interesting challenges that this encompasses.
>>> One of these challenges is the management of translation caches across 
>>> multiple cores which may sometimes require TLB shootdowns.
>>>
>>> A quick summary of TLB shootdowns: process P has two threads T1 and T2 
>>> that run on cores A and B respectively.
>>> When T1 makes a request to deallocate a page, core A has to ensure that 
>>> T2 on core B invalidates some cached translations before it marks the page 
>>> as free, lest B (executing T2) might find the translation in cache and 
>>> write to a page that T2 no longer should have access to.
>>>
>>> My current approach to this is that instead of using a `sync.Mutex` and 
>>> `Lock()`, I have created a custom `UntilLock(f func())` which takes in 
>>> a function that should be repeatedly executed until the lock is acquired.
>>>
>>> Current implementation: https://pastecord.com/hofoloxuru.go
>>>
>>> Notice that every mutex where a `Lock`-`Unlock`-pair might wrap an 
>>> interrupt, has to use the `UntilLock` as a `Lock` might cause the 
>>> goroutine to block and never check interrupts.
>>> The issue is further complicated if one resource R might be locked in 
>>> function F and another resource S is locked in function G.
>>> In this case, goroutine A might enter F, successfully acquire R, then 
>>> signal goroutine B.
>>> At the same time, B enters G, successfully acquires S, then signals 
>>> goroutine A.
>>> We have a deadlock.
>>> This is pretty easily resolved though as we just add the interrupt check 
>>> to the loop where F and G wait for the other goroutine to execute their 
>>> handlers.
>>>
>>> An idea came to me though...
>>> Life would be much easier if Go had signalling between goroutines.
>>> One could extend the syntax as `h := go f() handler` where `handler`
>>> would have the signature: `func handler(signal int)`
>>> You would then signal it with something like `s := sig h 1` to send a 
>>> signal of 1 to the goroutine associated with h.
>>>
>>> Idea for how code would look with signalling: 
>>> https://pastecord.com/qunyqejexo.go
>>>
>>> This would completely eliminate the issue I am currently facing and 
>>> though I'm no expert in the field, I believe Go's scheduler could make the 
>>> implementation require very little effort and the overhead would be 
>>> practically non-existent.
>>> This signalling would look a lot like OS signals and is a powerful 
>>> feature that isn't too complex in my opinion.
>>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/1766a0dc-5f14-43f0-81a7-44f2fa0b58d0n%40googlegroups.com.

Reply via email to