Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

single instance of package/mutex in a build?

90 views
Skip to first unread message

Jason E. Aten

unread,
May 11, 2025, 12:44:58 AMMay 11
to golang-nuts
Is there a way to insure I've only got one version
of a package in a build?  I need to make sure
a single mutex is used by all goroutines.

Detailed back story is here:

Robert Engels

unread,
May 11, 2025, 12:56:01 AMMay 11
to Jason E. Aten, golang-nuts
I don’t think a Go program can have two different version of the same package. That is what go.mod ensures and why most (all?) Go builds require access to all source code. 

On May 10, 2025, at 11:45 PM, Jason E. Aten <j.e....@gmail.com> wrote:


--
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...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/bdc40509-6b71-469e-82ce-c5fb4a64211bn%40googlegroups.com.

Robert Engels

unread,
May 11, 2025, 12:57:10 AMMay 11
to Jason E. Aten, golang-nuts
I think adding a project to your issue that demonstrates the problem would make things easier to understand. 

On May 10, 2025, at 11:45 PM, Jason E. Aten <j.e....@gmail.com> wrote:


--

Axel Wagner

unread,
May 11, 2025, 3:07:50 AMMay 11
to Jason E. Aten, golang-nuts
The only case where you can reasonably say that there are "two versions of the same package" in a build, is if they are in different major versions of the same module¹, say example.com/foo/v2 and example.com/foo/v3.
In that context, you have control over both versions. You can guarantee that they use the same mutex, by having them refer to each other. That is, you would implement v2 in terms of v3, that way ensuring that even if some code still imports v2, they still ultimately use the v3 mutex.
However, there is a problem: You can only implement v2 in terms of v3, once that exists. So you might introduce v3 while v2.23 is out and then have to introduce v2.24 to be implemented in terms of v3. But some clients might still import an v2.23. If those builds than *also* add v3, you end up with two mutexes.
The way around that is to have both of those refer to a third package, which contains the mutex. So you would have example.com/internal/mutex (which lives in its own module example.com/internal) which contains a mutex and have example.com/foo/v...@v2.24 import that to refer to the mutex. You can then wait until all importers have upgraded to v2.24. And when you then build example.com/foo/v3, it also imports example.com/internal/mutex. That way, all builds use a single mutex.
Of course, there is still a rest risk of clients not upgrading from v2.23 at all. But unfortunately, if you can't *somehow* prepare the earlier version and rely on that preparation to propagate, you are out of luck, I believe.

I don't believe there is a trick to statically enforce that two packages can not be imported at the same time (neither in the general case, nor in the special case that they are in two major versions of the same module). In a way, that's by design. It would be undesirable, for the import of one package to be able to break the import of a different package. Because if you *could* get into a situation where package A and package B are mutually exclusive, you could have a module dependency graph E->C->A, E->D->B and then the author of module E could do nothing to fix their build (as they can not force the authors of either module C or module D to change their imports, in a timely manner). Preventing these kinds of situations was a major design goal of Go modules. Which is why it's possible to use two major versions of a module in the same build in the first place.

You could potentially dynamically enforce that there are no two major versions of the same module used in one build. A simple (somewhat limited) way would be to use `runtime/debug.BuildInfo` to check which modules are included in an `init` function and panic/exit if there is a conflict. But I would strongly warn against that. Because as I said, making this impossible statically was done for a reason and those reasons still apply if you do it dynamically - just that it's even more frustrating, because you will only notice *after* building and potentially deploying the binary.

So, I think the real advice is: Don't. Do not rely on the assumption, that the same mutex is used. If nothing else, you can't prevent someone from forking your module and someone else to end up with both the original in the fork in the build. And if your case would break, that would break as well.

[1] Some may argue that this is not "the same package", as different major versions can be considered different modules and because Semantic Import Versioning is intentionally part of the module design. However, I think in the context of the question, that's still reasonably "the same package".

--

Jason E. Aten

unread,
May 11, 2025, 3:49:06 AMMay 11
to golang-nuts
Excellent. Thank you, Axel, for the full briefing, and backstory, and especially the advice to use a very stable internal style package to pro-actively avoid the situation. I'll close out that ticket.

Jason E. Aten

unread,
May 11, 2025, 4:22:41 AMMay 11
to golang-nuts
Thanks for the reply, Robert. I had thought, mistakenly that v1.6 and v1.7, of v0.8 and v1.0 
might collide, and I was concerned about having multiple instances of a what should be a global mutex.

But it turns out, when I tested this, and as Axel kindly pointed out, the module system will
always upgrade or downgrade everything together _unless_ you have v2 vs v1 situation.
It takes a major number difference to induce a two copy situation.

I've tried to read RSC's article on Semantic Import Versioning, but I find it is
vastly too long, and much too reliable a soporific. I'm thankful for Axel's salient summary and advice.

Robert Engels

unread,
May 11, 2025, 10:27:42 AMMay 11
to Jason E. Aten, golang-nuts
Yes, what Axel said is what I was referring to - with the last clause in the affirmative that I don’t consider them the same package. But using a stable third package to enforce a single instance works and is what I would do. 

On May 11, 2025, at 3:23 AM, Jason E. Aten <j.e....@gmail.com> wrote:

Thanks for the reply, Robert. I had thought, mistakenly that v1.6 and v1.7, of v0.8 and v1.0 
--
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...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages