Go 中可别用复制锁,会有这些大问题!

assignment copies lock value

assignment copies lock 错误

当我们用 go vet 检查静态问题的时候,你是否遇到 noCopy 相关的错误。最典型的就是 lock 的变量,测试代码:

静态检查的时候报错:

遇到这种语法提示可不能大意,一定要解决。以免给以后埋坑。那童鞋是否想过,为什么锁不能拷贝?

为什么锁不能拷贝?

很多时候我们被直接告诉了答案,Mutex 锁不能拷贝。但是原因呢?

划重点:变量资源本身带状态且操作要配套的不能拷贝。

比如说 Mutex 锁,WaitGroup ,这些本身都是带状态的信息的。并且它们操作一定要配套,不然就会死锁。什么意思?

锁的配套:

WaitGroup 的配套:

这种一定要严格的匹配,一次加锁对应一次解锁,数量不对就会出问题。

回到问题本身,为什么锁不能拷贝?

因为拷贝了很容易会导致操作次数不匹配。很容易理解,拷贝就是创建了一个新的变量,旧的变量加了锁,新的变量解锁。牛头不对马嘴,岂不就死锁了。就算不死锁也可能达不到锁互斥的目的。看个简单的例子:

上面就死锁了。有童鞋可能会反驳,我怎么可能犯这种低级错误。那再来一个:

这种运行起来也是报错的,因为锁变量的拷贝导致加锁解锁不是配套的操作。

再举个例子,看一个 WaitGroup 很典型的死锁问题:

上面的 wg 使用也会导致死锁。归根结底就是 WaitGroup 的变量操作没配套 。

划重点:针对需要配套操作的变量类型,基本上都会要求 noCopy 的,否则拷贝出来就乱套了。

在上万行的业务代码场景,可能你的问题更隐蔽。很悲伤的是,上面的三个例子都能正常编译。好消息是,上面三个例子都能用 go vet 检查出来。所以大家一定要善用 go vet 来配合检查,能够过滤大部分的低级问题。

Go 没有更好的办法阻止拷贝对象,于是通过了一个 noCopy 的机制。这个不能阻止编译,但是可以让 go vet 能再静态检查的时候检查出来。Go 里面通过实现一个 noCopy 的结构体,然后嵌入这个结构体就能让 go vet 检查出来。

类似于 Mutex,WaitGroup ,变量嵌入了这个 noCopy 的类型,就能被 go vet 检查出来。

通常业务如果也有不让拷贝的需求,会怎么做呢?

最简单的是:嵌入 sync.Mutex 变量。就能让这个变量也具有 noCopy 的功能。

哪些类型不能拷贝?

比如 Mutex Lock,Cond,Pool,WaitGroup 。这些资源都严格要求操作要配套:

不配套的操作,就会出问题。

类似于 Mutex Lock,WaitGroup 等资源一定要 操作配套 ,加锁一定对应解锁。否则牛头不对马嘴就一定会出问题;

“拷贝” 就是出现这种不一致的源头之一。所以 noCopy 就是解决这个问题的方案;

Go 没有更好的办法阻止你 Copy,没法在编译的时候阻止。但提供了 noCopy 机制,让程序员可以在 go vet 检查出来;

go vet 检查出的问题一定要慎重,一个不漏的解决它们;

点赞、在看  是对作者最大的支持。

扫码关注公众号「网管叨bi叨」

给网管个星标,第一时间吸我的知识 👆

网管整理了一本《Go 开发参考书》收集了70多条开发实践。去公众号回复【gocookbook】领取!还有一本《k8s 入门实践》讲解了常用软件在K8s上的部署过程,公众号回复【k8s】即可领取!

觉得有用就点个在看  👇👇👇

assignment copies lock value

请填写红包祝福语或标题

assignment copies lock value

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

assignment copies lock value

Get the Reddit app

Ask questions and post articles about the Go programming language and related tools, events etc.

[Question] Dealing with return copies lock value - pointer to sync.Mutex?

I've been playing with SFTP S3 proxy implementation by moriyoshi , specifically trying to resolve linter errors. One of those is:

The most obvious solution I saw was to make mutex a pointer here: https://github.com/yvlasenko/s3-sftp-proxy/blob/8fb8a200c223aa8204de2ff76b31ff315afafd9c/phantom_object_map.go#L13

That did make linter happy but application crashed:

If anyone could point me why that happens and what would be the solution here please?

By continuing, you agree to our User Agreement and acknowledge that you understand the Privacy Policy .

Enter the 6-digit code from your authenticator app

You’ve set up two-factor authentication for this account.

Enter a 6-digit backup code

Create your username and password.

Reddit is anonymous, so your username is what you’ll go by here. Choose wisely—because once you get a name, you can’t change it.

Reset your password

Enter your email address or username and we’ll send you a link to reset your password

Check your inbox

An email with a link to reset your password was sent to the email address associated with your account

Choose a Reddit account to continue

assignment copies lock value

Beware of copying mutexes in Go

Suppose we have a struct that contains a map, and we want to modify the map in a method. Here's a simple example :

A Container holds a map of counters, keyed by name. Its inc method increments the specified counter (let's assume that the counter already exists). main calls inc many times in a loop.

If we run this snippet, it will print out:

Now say that we want two goroutines to call inc concurrently. Since we are wary of race conditions, we'll use a Mutex to lock around the critical region:

What would you expect the output to be? I get something like this:

We were careful to use a mutex, so what went wrong? Can you see how to fix it? Hint: it's a single-character code change!

The problem with the code is that whenever inc is called, our container c is copied into it, because inc is defined on Container , not *Container ; in other words, it's a value receiver , not a pointer receiver . Therefore, inc can't really modify the contents of c per se.

But wait, how did the original sample work then? In the single-goroutine sample, we passed c by value too, but it worked - main observed the changes to the map done by inc . This is because maps are special - they are reference types, not value types. What's stored in Container is not the actual map data, but a pointer to it. So even when we create a copy of the Container , its counters member still contains the address of the same data.

So the original code sample is wrong too. Even though it works, it goes against the guidelines ; methods that modify the object should be defined on pointers, not values. Using a map here leads us to a false sense of security. As an exercise, try to replace the map with just a single int counter in the original example, and notice how inc increments a copy of it, so that in main its effects will not be seen.

The Mutex is a value type (see definition in Go's source , including the comment that explicitly asks not to copy mutexes), so copying it is wrong. We're just creating a different mutex, so obviously the exclusion no longer works.

The one-character fix is, therefore, to add a * in front of Container in the definition of inc :

Then c is passed by pointer into the method, and actually refers to the same instance of Container in memory as the one the caller has.

This is not an uncommon problem! In fact, go vet will warn about it:

It often comes up in scenarios like HTTP handlers, which are invoked concurrently without the programmer's explicitly writing any go statements. I'll write more about this in a future post.

This issue really helps clarify the difference between value and pointer receivers in Go, in my opinion. To drive the point home, here's another code sample, unrelated to the last two. It leverages Go's ability to create pointers to objects using & and examine their addresses with the %p formatting directive:

Its output is (in one particular run on my machine - for you the addresses may be different, though the relations between them should be the same):

The main function creates a Container and prints out its address and the address of field s . It then invokes two Container methods.

byValMethod has a value receiver, and it prints out different addresses because it gets a copy of c . On the other hand, byPtrMethod has a pointer receiver and the addresses it observes are identical to the ones in main , because it takes the address of the actual c when invoked, not a copy.

For comments, please send me an email .

Source file src / cmd / vendor / golang.org / x / tools / go / analysis / passes / copylock / copylock.go

View as plain text

  • Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
  • Advertising & Talent Reach devs & technologists worldwide about your product, service or employer brand
  • OverflowAI GenAI features for Teams
  • OverflowAPI Train & fine-tune LLMs
  • Labs The future of collective knowledge sharing
  • About the company Visit the blog

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Get early access and see previews of new features.

Why does assignment in Go create a copy?

I will clarify the question a bit. I have read (almost completely) the Go specification, FAQ, Effective Go, and, of course, Tour of Go.

I know that Go is a "pass by value" language and even managed to reason about this behavior and understand all the implications.

All assignments in Go also create copies. In some cases, it's just a value, in some -- a pointer. For some data structures, it's a bit trickier in that the whole structure is copied and might include an implicit pointer to another data structure.

The question is: what in the language specification says explicitly that assignments always create copies?

I feel like it doesn't even need to be mentioned once you understand that there are no references in Go, but the section on assignment statements in the specification doesn't even mention the pass-by-value semantics.

I feel like there must be something in the documentation that describes the behavior in detail, and I, due to lack of some foundational misunderstanding, fail to realize the explanation is there.

  • variable-assignment
  • pass-by-value

blackgreen's user avatar

  • Perhaps the confusion comes from conflating "pass by value" and "copy". Go is pass-by-value, which is rather more simple than "copying' which is a very broad term. –  Hymns For Disco Commented Aug 7, 2021 at 20:46
  • For example, the FAQ document says that "a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter". This covers the behavior of function calls, but the assignment itself is not described at all. I am just wondering whether this is too obvious to be documented in detail or I am missing something in the docs –  Don Draper Commented Aug 7, 2021 at 20:49
  • Could you explain further why some data structures are "a bit trickier" in terms of copying? –  Hymns For Disco Commented Aug 7, 2021 at 21:00
  • I might have used the wrong phrasing. What I meant was that it takes time to understand that with slices, what is copied is the slice header. There is nothing tricky here and it's only a matter of reading the official blog posts on how slices and maps are implemented. This has nothing to do, though, with my question :) –  Don Draper Commented Aug 7, 2021 at 21:04
  • Why does assignment in Go create a copy? strictly speaking, design decision. think your queston is better titled, where in the spec does it mention that the language passes by value ; –  user4466350 Commented Aug 8, 2021 at 20:07

3 Answers 3

What in the language specification says explicitly that assignment always creates copies?

Nothing explicit, but you can maybe deduce this from Variables , which nicely addresses also the case of function signatures:

A variable declaration or, for function parameters and results, the signature of a function declaration or function literal reserves storage for a named variable.

If storage is reserved, when later you assign the result of a unary expression to it — e.g. another variable —, then it must be a copy, otherwise you would have memory aliasing. Which is what Dave Cheney is talking about in There is no pass-by-reference in Go .

Unlike C++, each variable defined in a Go program occupies a unique memory location.

This also has one more important implication, which is the zero value . If you don't provide an expression to initialize a variable in its declaration, the storage reserved for it is given the zero value as default value.

Without going into too much detail, these excerpts from the spec should provide some clarity:

A variable is a storage location for holding a value. A variable's value [...] is the most recent value assigned to the variable.

At the language level, defining "copying" of values isn't really necessitated. The important implication of copying as we commonly understand it, is that modifying the "copied to" value will not alter the "copied from" value *. This property can be inferred from the above quotations.

  • (important to note here that "value" and "data structure" are not the same thing. A data structure may be comprised of multiple values.)

Hymns For Disco's user avatar

The spec actually explicitly talks about this here:

https://golang.org/ref/spec#Calls

In particular:

After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the caller when the function returns.

About assignments: All assignments in most of the languages I know of create copies of values (see Python exception in the comments). A copy of the value on the RHS is assigned to the LHS. If the RHS is a pointer, then a copy of that pointer is assigned to the LHS.

Burak Serdar's user avatar

  • 1 Assignments in Python do not copy anything. They just create bindings –  Don Draper Commented Aug 8, 2021 at 5:20

Your Answer

Reminder: Answers generated by artificial intelligence tools are not allowed on Stack Overflow. Learn more

Sign up or log in

Post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged go variable-assignment pass-by-value or ask your own question .

  • Featured on Meta
  • We've made changes to our Terms of Service & Privacy Policy - July 2024
  • Bringing clarity to status tag usage on meta sites
  • Feedback requested: How do you use tag hover descriptions for curating and do...

Hot Network Questions

  • Why would an incumbent politician or party need to be re-elected to fulfill a campaign promise?
  • Is there a law against biohacking your pet?
  • What does "off" mean in "for the winter when they're off in their southern migration breeding areas"?
  • A "clearly" won queen endgame
  • Did the United States have consent from Texas to cede a piece of land that was part of Texas?
  • False Color Objects by Size
  • On the definition on almost sure convergence
  • Is my encryption format secure?
  • Do temperature variations make trains on Mars impractical?
  • What is the origin of this quote on telling a big lie?
  • Why is the identity of the actor voicing Spider-Man kept secret even in the commentary?
  • What do smooth signatures give you?
  • General equation to calculate time required to travel a distance given initial speed and constant acceleration
  • one of my grammar books written by a Japanese teacher/Japanese teachers
  • Is there an efficient way to extract a slice of a 3d array?
  • Generating Carmichaeal numbers in polynomial time
  • Kyoto is a famous tourist destination/area/site/spot in Japan
  • Why in QM the solution to Laguerre equations are ONLY Laguerre polynomials?
  • How can I cover all my skin (face+neck+body) while swimming outside (sea or outdoor pool) to avoid UV radiations?
  • Where exactly was this picture taken?
  • John 1:1, "the Word was with God, and the Word was God." Vs2, He was in the beginning with God. Does this not prove Jesus existed before Genesis 1:1?
  • How did this zucchini plant cling to the zip tie?
  • Easyjet denied EU261 compensation for flight cancellation during Crowdstrike: Any escalation or other recourse?
  • Optimal Algorithm to Append and Access Tree Data

assignment copies lock value

Navigation Menu

Search code, repositories, users, issues, pull requests..., provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications You must be signed in to change notification settings

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

protoc-gen-go: new message struct structure prevents struct copying in Go #1079

@tsimionescu

tsimionescu commented Apr 13, 2020 • edited Loading


protoc-gen-go v1.20.0, protoc v3.11.4


Re-generating code with the newer protoc-gen-go breaks code that is copying generated message structs. Go vet is showing the following error:

Previous generated code for a simple struct:

Newly generated code, including the indirect reference to sync.Mutex:

The new message struct should be copy-able, or at least an explicit backwards compatibility change should be highlighted in the release notes.

@tsimionescu

dsnet commented Apr 13, 2020 • edited Loading

This is working as intended. Generated messages should not shallow-copied since they posses fields that are accessed atomically. This was already the case prior to the new release and has been so for several years now.

If you look at the type, you'll see that it embeds a type, which is just a zero-sized array of . The allows Go vet to flag what was already a pre-existing problem.

To copy a message, one should use .

Sorry, something went wrong.

tsimionescu commented Apr 13, 2020

This is somewhat surprising, since the old structs were often very simple Go structs, and I don't really understand what you mean by

I would be grateful if you could point me to some more information on this topic of atomic access vs struct copying.

Regardless, I have not been able to find any mention of this restriction in the docs, so if this was already assumed and it is now explicitly enforced, this is a clear positive change then. I will close the issue then.

Thank you for the quick reply!

  • 👍 2 reactions

dsnet commented Apr 13, 2020

The old XXX_sizecache, XXX_extensions, and others are examples of fields that are accessed atomically.

In general, its not safe to shallow copy any struct type where you don't control the implementation since you have no guarantee that you aren't accidentally copying atomic variables and locks (which is exactly the case here). This expectation is not specific to protobufs, but more of a general idiom.

No branches or pull requests

@dsnet

IMAGES

  1. Value Lock-In Notes 2021 (Public Version)

    assignment copies lock value

  2. How to Lock Cell Value Once Calculated in Excel (3 Simple Ways)

    assignment copies lock value

  3. How to lock a value in Excel using VLOOKUP

    assignment copies lock value

  4. How to Lock Cell Value Once Calculated in Excel (3 Simple Ways)

    assignment copies lock value

  5. Configuring the Assignment Submission Lock in Canvas LTI 1.1

    assignment copies lock value

  6. Configuring the Assignment Submission Lock in Canvas LTI 1.1

    assignment copies lock value

COMMENTS

  1. How to get around assignment copies lock value to tr: net/http

    @Nilesh, there are lots of valid statements which are not logical. Unfortunately you can't readily see the mutex field from the docs, so this may not be obvious at first glance, but because the Transport type has all pointer receivers, should always be reused, and is safe for concurrent access, it's very unlikely that someone would choose a non-pointer value.

  2. Go Programming: Preventing Unintended Structure Copies with ...

    assignment copies lock value to wg2: sync.WaitGroup contains sync.noCopy. How does the IDE achieve this? Can we use this mechanism to tell others not to copy a particular structure?

  3. Go 中可别用复制锁,会有这些大问题!-CSDN博客

    Go 没有更好的办法阻止拷贝对象,于是通过了一个 noCopy 的机制。. 这个不能阻止编译,但是可以让 go vet 能再静态检查的时候检查出来。. Go 里面通过实现一个 noCopy 的结构体,然后嵌入这个结构体就能让 go vet 检查出来。. type noCopy struct {} // Lock is a no-op used by ...

  4. cmd/vet: vet is reporting lock value copying because of a Lock method

    main.go:11: assignment copies lock value to b: main.A main.go:12: assignment copies lock value to _: main.A exit status 1 $ The text was updated successfully, but these errors were encountered: All reactions. Copy link Contributor. JayNakrani commented Dec 28, 2016. vet checks ...

  5. go vet : Assignment copies lock value #5758

    Grove Current behavior: go vet -copylocks ./... finds 1 issue: MessageLine of code assignment copies lock value to newTransport: net/http.Transport contains sync.Mut... I'm submitting a ... bug report Traffic Control components affected ... Grove Current behavior: go vet -copylocks ./... finds 1 issue: MessageLine of code assignment copies lock ...

  6. Go: Vet Command Is More Powerful Than You Think

    func main() {var lock sync.Mutex l := lock l.Lock() l.Unlock()} from vet: main.go:9:7: assignment copies lock value to l: sync.Mutex A struct that uses a lock should be used by pointer in order to ...

  7. cmd/vet: vet is reporting lock value copying on composite literal

    go vet is reporting "assignment copies lock value to r: hipache.hipacheRouter" in the last line. The type hipacheRouter indeed contains a lock: ... Generally, it is not allowed to copy a lock by value. It is however safe to copy a zero value lock by value, as a special case and the only one that does not lead to strange results. This is what ...

  8. [Question] Dealing with return copies lock value

    There are two choices for putting a mutex into a struct when considering the restriction that a mutex cannot be copied: Have a sync.Mutex as a plain field (no * ), and ensure every reference to an instance of that struct is a pointer (or is on the local stack, a la var mu sync.Mutex ). Make it a *sync.Mutex and initialize it somewhere so that ...

  9. cmd/vet: copylocks false positives · Issue #16227 · golang/go

    Types.NewStruct constructs a new struct and omits (implicitly zeros) the lock fields. I think we should consider rolling back the new checks for 1.7 and restoring them in 1.8 without these false positives. At least in the case of struct initialization, ignoring fields being initialized to the zero value will help.

  10. go/analysis/passes/copylock/copylock.go

    const Doc = `check for locks erroneously passed by value: Inadvertently copying a value containing a lock, such as sync.Mutex or: sync.WaitGroup, may cause both copies to malfunction. Generally such: values should be referred to through a pointer.` var Analyzer = &analysis.Analyzer{Name: "copylocks", Doc: Doc,

  11. Beware of copying mutexes in Go

    The Mutex is a value type (see definition in Go's source, including the comment that explicitly asks not to copy mutexes), so copying it is wrong. We're just creating a different mutex, so obviously the exclusion no longer works. The one-character fix is, therefore, to add a * in front of Container in the definition of inc: Then c is passed by ...

  12. - The Go Programming Language

    go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic. Learn more.

  13. go vet: assignment copies lock value to buildLock #3377

    hello, i want to build fabric in my ubuntu, but i get some problems that i cant find on internet. i follow the steps in the doc. In last topic "Install the development tools", after my typing make basic-checks integration-test-prereqs, i...

  14. - The Go Programming Language

    Leave result checking 168 // to the return statement. 169 } 170 171 // checkCopyLocksRange checks whether a range statement 172 // might inadvertently copy a lock by checking whether 173 // any of the range variables are locks. 174 func checkCopyLocksRange(pass *analysis.Pass, r *ast.RangeStmt) { 175 checkCopyLocksRangeVar(pass, r.Tok, r.Key ...

  15. go

    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.) a, _ := NewGroups() a.Lock() lockShared(a) fmt.Println("done")

  16. go bug sync.Map · Issue #32641 · golang/go · GitHub

    Map, a multi-threaded, secure Map officially provided by Go, because of the need for multi-threading. Because of the need, I use a two-dimensional sync. Map, that is, sync. Map will contain another pile of sync. Map, at this time, very strange things happened, sync.

  17. Copy lock inspection : GO-5764

    {{ (>_<) }}This version of your browser is not supported. Try upgrading to the latest stable version. Something went seriously wrong.

  18. Why does assignment in Go create a copy?

    I know that Go is a "pass by value" language and even managed to reason about this behavior and understand all the implications. All assignments in Go also create copies. In some cases, it's just a value, in some -- a pointer. For some data structures, it's a bit trickier in that the whole structure is copied and might include an implicit ...

  19. protoc-gen-go: new message struct structure prevents struct copying in

    In general, its not safe to shallow copy any struct type where you don't control the implementation since you have no guarantee that you aren't accidentally copying atomic variables and locks (which is exactly the case here). This expectation is not specific to protobufs, but more of a general idiom.