FAQ
Effective Go <http://golang.org/doc/effective_go.html#sharing> emphasizes
Go's "Share by communicating" philosophy:

Go encourages a different approach in which shared values are passed around
on channels and, in fact, never actively shared by separate threads of
execution. Only one goroutine has access to the value at any given time.
Data races cannot occur, by design. To encourage this way of thinking we
have reduced it to a slogan:



Do not communicate by sharing memory; instead, share memory by
communicating.


I delved a little bit into the Go build tool's source code in order to
explore some idiomatic Go and to learn from it. But I was sort of
surprised by what I came across almost immediately, at a pretty high level
of the code -- some code that didn't seem to follow the above philosophy.
  I would love to hear some thoughts on why the code discussed below is
written the way it is, considering that it doesn't really seem to me to be
following Go best practices.

build.go <https://code.google.com/p/go/source/browse/src/cmd/go/build.go> is
one of the first files that runs when you do a go build. It is a very
high-level file that is just building up a queue of work to be done, and
then going through that queue to dispatch jobs.

The builder struct<https://code.google.com/p/go/source/browse/src/cmd/go/build.go#356>
  has two variables of type sync.Mutex: one called output and one called exec.
Even though the file, in general, is well-commented, neither of these
mutexes has a comment explaining when to use it, but you can figure it out
from the source; but there is room for error there.

The code that uses the mutexes seems to be "communicating by sharing
memory," contrary to Go's recommendation:

    -

    showcmd()<https://code.google.com/p/go/source/browse/src/cmd/go/build.go#1190> does
    this:

      func (b *builder) showcmd(dir string, format string, args ...interface{}) {
              b.output.Lock()
              defer b.output.Unlock()
              b.print(b.fmtcmd(dir, format, args...) + "\n")
      }

    Well, that is just old-school, and is ripe with potential for bugs. It
    is too easy to forget to call b.output.Lock() and b.output.Unlock() —
    or, for that matter, even to know that you are supposed to. (The
    showOutput() and function right below that uses the same mutex and has
    the same issue.)
    -

    More interesting for my argument is the other mutex, exec. If you search
    down the page for exec.Lock, you will find three uses of it. Two of the
    calls are in func do()<https://code.google.com/p/go/source/browse/src/cmd/go/build.go#636>,
    a very long function that calls exec.Lock() right before a bunch of
    code. What is it protecting? Well, I’m not sure — you’d probably have to
    read the code pretty carefully to figure it out. (The second call in that
    function is easier to figure out, but still error-prone.)

Is there a reason the code was written this way, with mutexes, instead of
using higher-level Go language constructs?

Thanks - Mike Morearty

--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

  • Keith Rarick at Dec 5, 2013 at 2:17 am

    On Wed, Dec 4, 2013 at 4:57 PM, wrote:
    The builder struct has two variables of type sync.Mutex: one called output
    and one called exec. Even though the file, in general, is well-commented,
    neither of these mutexes has a comment explaining when to use it, but you
    can figure it out from the source; but there is room for error there.
    I believe the convention is to group the mutex
    with the fields it guards.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Andrew Gerrand at Dec 5, 2013 at 2:37 am
    Keith is on the money about the grouping of the fields.
    That's the convention, and to me the code is clear (I didn't write it).
    On 5 December 2013 11:57, wrote:

    Is there a reason the code was written this way, with mutexes, instead of
    using higher-level Go language constructs?

    Sometimes a mutex is a cleaner way of protecting a shared resource.
    You could do it with channels in this case, but it would probably be more
    verbose.
    And there are other situations where channels are clearly a better approach.
    Go gives you the choice.

    Andrew

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • GreatOdinsRaven at Dec 6, 2013 at 3:50 am
    There is nothing wrong with using a mutex in Go where appropriate. You
    *could* use channels here, but it'd be more verbose and add no clear
    benefit. Sometimes, a simple mutex that protects a shared resource gets the
    job done just as good. Another example is multiple goroutines modifying the
    same map. You *could* communicate "I want ownership of this resource now"
    over a channel and wait for "ok I'm done with it, you have it" - but then
    you're basically implementing mutex semantics using channels, which
    is…overkill.

    The way I think about it is, if I need to protect access to a shared
    resource and nothing else - a mutex is fine. If I want to *communicate *-
    channels*.*
    On Wednesday, December 4, 2013 5:57:44 PM UTC-7, mi...@morearty.com wrote:

    Effective Go <http://golang.org/doc/effective_go.html#sharing> emphasizes
    Go's "Share by communicating" philosophy:

    Go encourages a different approach in which shared values are passed
    around on channels and, in fact, never actively shared by separate threads
    of execution. Only one goroutine has access to the value at any given time.
    Data races cannot occur, by design. To encourage this way of thinking we
    have reduced it to a slogan:



    Do not communicate by sharing memory; instead, share memory by
    communicating.


    I delved a little bit into the Go build tool's source code in order to
    explore some idiomatic Go and to learn from it. But I was sort of
    surprised by what I came across almost immediately, at a pretty high level
    of the code -- some code that didn't seem to follow the above philosophy.
    I would love to hear some thoughts on why the code discussed below is
    written the way it is, considering that it doesn't really seem to me to be
    following Go best practices.

    build.go <https://code.google.com/p/go/source/browse/src/cmd/go/build.go> is
    one of the first files that runs when you do a go build. It is a very
    high-level file that is just building up a queue of work to be done, and
    then going through that queue to dispatch jobs.

    The builder struct<https://code.google.com/p/go/source/browse/src/cmd/go/build.go#356>
    has two variables of type sync.Mutex: one called output and one called
    exec. Even though the file, in general, is well-commented, neither of
    these mutexes has a comment explaining when to use it, but you can figure
    it out from the source; but there is room for error there.

    The code that uses the mutexes seems to be "communicating by sharing
    memory," contrary to Go's recommendation:

    -

    showcmd()<https://code.google.com/p/go/source/browse/src/cmd/go/build.go#1190> does
    this:

    func (b *builder) showcmd(dir string, format string, args ...interface{}) {
    b.output.Lock()
    defer b.output.Unlock()
    b.print(b.fmtcmd(dir, format, args...) + "\n")
    }

    Well, that is just old-school, and is ripe with potential for bugs. It
    is too easy to forget to call b.output.Lock() and b.output.Unlock() —
    or, for that matter, even to know that you are supposed to. (The
    showOutput() and function right below that uses the same mutex and has
    the same issue.)
    -

    More interesting for my argument is the other mutex, exec. If you
    search down the page for exec.Lock, you will find three uses of it.
    Two of the calls are in func do()<https://code.google.com/p/go/source/browse/src/cmd/go/build.go#636>,
    a very long function that calls exec.Lock() right before a bunch of
    code. What is it protecting? Well, I’m not sure — you’d probably have to
    read the code pretty carefully to figure it out. (The second call in that
    function is easier to figure out, but still error-prone.)

    Is there a reason the code was written this way, with mutexes, instead of
    using higher-level Go language constructs?

    Thanks - Mike Morearty
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedDec 5, '13 at 1:31a
activeDec 6, '13 at 3:50a
posts4
users4
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase