I wonder if there is any idiomatic way to represent scoped semantics. By scoped I mean things like:
scoped mutex (oneliner instead of explicit Lock + deffered Unlock),
logging function (or any code block) entrance and exit,
measuring execution time.
Example code for first two bullets:
package main
import "log"
import "sync"
func Scoped(m *sync.Mutex) func() {
m.Lock()
return func() {
m.Unlock()
}
}
func Log(what string) func() {
log.Println(what, "started")
return func() {
log.Println(what, "done")
}
}
func main() {
defer Log("testing")()
m := &sync.Mutex{} // obviously mutex should be from other source in real life
defer Scoped(m)()
// use m
}
https://play.golang.org/p/33j-GrBWSq
Basically we need to make one function call just now (eg mutex lock), and one call should be postponed to defer (eg mutex unlock). I propose just returning unnamed function here, but it can be easily named (return struct with function field).
There is only one problem: user can forget to 'call' result of first call.
This code is (can be) idiomatic?
Take anonymous function as a scope:
func() {
Entrance()
defer Exit()
// anything you want to do in this scope
}()
Your proposed solution is already nice. You return a value of func type which you also have to call at the end of the defer.
You can avoid that (returning a func value), but there have to be 2 function calls, one that logs the start event and another one that logs the end event.
The alternative is to make a function call which produces the parameter value of the function that is deferred (rather than returning a function) which is evaluated with the defer statement, and this way it still can remain one line.
You can also try it on the Go Playground:
func start(s string) string {
fmt.Println("Started", s)
return s
}
func end(name string) {
fmt.Println("Ended", name)
}
func main() {
defer end(start("main"))
fmt.Println("Doing main's work...")
}
Output:
Started main
Doing main's work...
Ended main
I do not believe there is an idiomatic way to do this. I'm not sure why you'd want to either, is it really so bad to write
m.Lock()
defer m.Unlock()
?
I think question isn't relevant to Go idiomaticity, Seems it's generally better to reason about code when function behave identically either call. To keep state I'd better make an object and define function as method on that object. Means something like
type message string
func (foo message) Log(bar string){
if bar==nil{doSomethingSpecial()}
switch foo{
case something: doSomething()
...
case nil: doSomethingInitial()
default: doDefault()
}
log.Println(bar, "started")
foo=bar
}
Related
My question might be dumb but please bear with me. If two go-routines are calling the same function, will they share variables in that function? Is it safe to declare variables inside the function and use freely?
func main() {
go func1(1)
go func1(2)
}
func func1(a int) {
something := a
// do something
}
In the above code when two go-routines are calling same function will they hinder with the variable declaration of each other? Will the value of something change if the go routines are not in order or something?
will they hinder the variable declaration - no. essentially it's a function.. so if you're declaring the variable inside the function.. there won't be any issues and it works normally.
but if the variable is not declared inside the function but outside the scope of the function then the order of the go routines will hinder the value
for example
import (
"fmt"
"time"
)
var something int
func test(a int) {
something += a
fmt.Println("something", something)
}
func main() {
fmt.Println("Testing Something")
go test(20)
go test(3)
time.Sleep(1 * time.Second) // crude way without using channels or waitgroup.
}
According the following function declarations from sync package:
Add -------> func (wg *WaitGroup) Add(delta int)
Done ------> func (wg *WaitGroup) Done()
Wait ------> func (wg *WaitGroup) Wait()
I understand that all 3 of them are called by a pointer to a WaitGroup, right?
If this is correct, I don't understand in the next pice of code, why Done function is called using a pointer variable, but Add and Wait functions are called using a variable (not a pointer):
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
}
Thanks a lot for your help.
Done, Add and Wait are called on pointer. All functions refer to a pointer receiver *WaitGroup. The fact that you declare variable as value of WaitGroup doesn't mean much as all those methods will all access and modify the variable. The only problem happens when you want to pass your variable to worker - if you try to pass it as value you will make a copy and then Done will be referring to different pointer than Add and Wait - that's why you pass it's address with &.
I think here is best explanation I so far seen on the topic: https://github.com/golang/go/wiki/MethodSets#variables
In general, when you have a variable of a type, you can pretty much call whatever you want on it. When you combine the two rules above together, the following is valid:
type List []int
func (l List) Len() int { return len(l) }
func (l *List) Append(val int) { *l = append(*l, val) }
func main() {
// A bare value
var lst List
lst.Append(1)
fmt.Printf("%v (len: %d)\n", lst, lst.Len())
// A pointer value
plst := new(List)
plst.Append(2)
fmt.Printf("%v (len: %d)\n", plst, plst.Len())
}
Note that both pointer and value methods can both be called on both pointer and non-pointer values. To understand why, let's examine the method sets of both types, directly from the spec:
List
- Len() int
*List
- Len() int
- Append(int)
Notice that the method set for List does not actually contain Append(int) even though you can see from the above program that you can call the method without a problem. This is a result of the second spec section above. It implicitly translates the first line below into the second:
lst.Append(1)
(&lst).Append(1)
Now that the value before the dot is a *List, its method set includes Append, and the call is legal.
To make it easier to remember these rules, it may be helpful to simply consider the pointer- and value-receiver methods separately from the method set. It is legal to call a pointer-valued method on anything that is already a pointer or whose address can be taken (as is the case in the above example). It is legal to call a value method on anything which is a value or whose value can be dereferenced (as is the case with any pointer; this case is specified explicitly in the spec).
Is this Get method buggy and prone to a theoretical data race?
type item struct {
val int
mutex sync.RWMutex
}
func (i *item) Set(val int) {
i.mutex.Lock()
defer i.mutex.Unlock()
i.val = val
}
func (i *item) Get() int {
i.mutex.RLock()
defer i.mutex.RUnlock()
return i.val
}
I ask because I saw a rare data race when running my tests with -race with the former code, but can't find any way of duplicating the effect.
Is it possible for i.val to be set to a different value between when the defer carries out the RUnlock, and when we read and return the value from the struct?
Must Get() be something like this instead?:
func (i *item) Get() int {
i.mutex.RLock()
defer i.mutex.RUnlock()
val := i.val
return val
}
Your code is safe, deferred functions are executed after the expression list of the return statement is evaluated. If you would have named result parameters, the return values would also be assigned to them before calling the deferred functions (and you could even modify the return values before "truly" returning from the enclosing function).
No need to create a local variable to store i.val.
Why RWMutex in conditional variable doesn't have RLock and RUnLock methods?
package main
import (
"sync"
)
func main() {
cond := &sync.Cond{L: &sync.RWMutex{}}
cond.L.RLock()
cond.L.RUnlock()
}
I got an error:
cond.L.RLock undefined (type sync.Locker has no field or method RLock)
cond.L.RUnlock undefined (type sync.Locker has no field or method RUnlock)
https://play.golang.org/p/LdGYlrQQx86
A sync.Cond only makes use of the methods required by a sync.Locker: Lock and Unlock. The minimal interface needed by Cond is therefore just those two methods.
You can still use your RWMutex directly (as opposed to going through Cond), and Cond will still work.
func main() {
myMutex := &sync.RWMutex{}
cond := sync.NewCond(myMutex)
// Use the RW mutex directly.
myMutex.RLock()
myMutex.RUnlock()
// Use the mutex through cond. Lock and Unlock only.
cond.L.Lock()
cond.L.Unlock()
}
Or you can use it through Cond.L after making sure it is of the expected type:
func main() {
cond := &sync.Cond{L: &sync.RWMutex{}}
// Typecheck cond.L.
myRWMutex, ok := cond.L.(*sync.RWMutex)
if !ok {
panic("AHHHH!!!")
}
myRWMutex.RLock()
myRWMutex.RUnlock()
}
(you can skip the type check and just say cond.L.(*sync.RWMutex) but if it isn't a sync.RWMutex, your program will panic)
I have the following structure
type Groups struct {
sync.Mutex
Names []string
}
and the following function
func NewGroups(names ...string) (Groups, error) {
// ...
return groups, nil
}
When I check for semantic errors with go vet, I am getting this warning:
NewGroups returns Lock by value: Groups
As go vet is shouting, it is not good. What problems can this code bring? How can I fix this?
You need to embed the sync.Mutex as a pointer:
type Groups struct {
*sync.Mutex
Names []strng
}
Addressing your comment on your question: In the article http://blog.golang.org/go-maps-in-action notice Gerrand is not returning the struct from a function but is using it right away, that is why he isn't using a pointer. In your case you are returning it, so you need a pointer so as not to make a copy of the Mutex.
Update: As #JimB points out, it may not be prudent to embed a pointer to sync.Mutex, it might be better to return a pointer to the outer struct and continue to embed the sync.Mutex as a value. Consider what you are trying to accomplish in your specific case.
Return a pointer *Groups instead.
Embedding the mutex pointer also works but has two disadvantages that require extra care from your side:
the zero value of the struct would have a nil mutex, so you must explicitly initialize it every time
func main() {
a, _ := NewGroups()
a.Lock() // panic: nil pointer dereference
}
func NewGroups(names ...string) (Groups, error) {
return Groups{/* whoops, mutex zero val is nil */ Names: names}, nil
}
assigning a struct value, or passing it as function arg, makes a copy so you also copy the mutex pointer, which then locks all copies. (This may be a legit use case in some particular circumstances, but most of the time it might not be what you want.)
func main() {
a, _ := NewGroups()
a.Lock()
lockShared(a)
fmt.Println("done")
}
func NewGroups(names ...string) (Groups, error) {
return Groups{Mutex: &sync.Mutex{}, Names: names}, nil
}
func lockShared(g Groups) {
g.Lock() // whoops, deadlock! the mutex pointer is the same
}
Keep your original struct and return pointers. You don't have to explicitly init the embedded mutex, and it's intuitive that the mutex is not shared with copies of your struct.
func NewGroups(names ...string) (*Groups, error) {
// ...
return &Groups{}, nil
}
Playground (with the failing examples): https://play.golang.org/p/CcdZYcrN4lm