Skip to content
New issue

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

Add an explicit Close function #21

Merged
merged 1 commit into from Sep 15, 2018
Merged

Conversation

virtuald
Copy link
Contributor

@virtuald virtuald commented Aug 7, 2018

  • Useful for long running processes that don't want to depend on the GC for cleanup

Copy link
Member

@theckman theckman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this contribution! I've found a few small things I think need to be tweaked before we merge this.

I like clean/linear git history that really reflects the changes being made. So when going through to address the comments, please amend your current commit instead of creating additional ones.

flock.go Outdated
f.m.Lock()
defer f.m.Unlock()

f.unlockInternal()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please handle the returned error here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you like to deal with it? It seems like the same type of thing as line 55.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case the most reasonable is likely to annotate the error and return it upward.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Well, would that be right? What happens if someone closes after Unlock()? Does it make sense to make it so that Close() just fails if Unlock() hasn't been called yet?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been doing some thinking about this, and from my perspective I think I see two possible ways to go about solving it. I'm coming at it from the perspective of wanting to know when unsafe data access may have been happening. If I hit an error when trying to release the exclusive lock I have on a file, that may indicate I've been silently corrupting data and may want to manually validate consistency.

So I think there're two ways we can make sure that information isn't lost:

  • The first would be to simply return the error from f.unlockInternal() if one occurs. I don't recall whether calling .Unlock() and then .Close() would result in f.unlockInternal() returning an error for on .Close(). If it does...
  • The second option is to disallow closing when the file is still locked. This would require consumers to be more thoughtful of how they consume the package, and to do the orchestration in the right order.

@virtuald Which path do you feel better aligns with the problem you're solving?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think disallowing closing when the file is still locked is unintuitive and would lead to difficult to diagnose issues (particularly if one did an unchecked defer lock.Close() which seems to be a common if ill-advised pattern).

According to fcntl(2):

If a process closes any file descriptor referring to a file, then all of the process's locks on that file are released, regardless of the file descriptor(s) on which the locks were obtained.

According to MSDN for LockFile:

If a process terminates with a portion of a file locked or closes a file that has outstanding locks, the locks are unlocked by the operating system. However, the time it takes for the operating system to unlock these locks depends upon available system resources. Therefore, it is recommended that your process explicitly unlock all files it has locked when it terminates. If this is not done, access to these files may be denied if the operating system has not yet unlocked them.

From both of these perspectives, I think it's a valid thing to just ignore the unlock error, as Close will also unlock the file. If any error should be returned, I would say the error returned from Close() should be returned as that's the semantic thing that's occurring.

flock_windows.go Outdated
}

// the internal mutex must be held for reading+writing when calling this function
func (f *Flock) unlockInternal() error {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the unnecessary whitespace after the method header.

flock_unix.go Outdated
}

// the internal mutex must be held for reading+writing when calling this function
func (f *Flock) unlockInternal() error {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this unnecessary whitespace after the method header.

flock.go Outdated
// Close will call Unlock() and close the underlying file descriptor. Use of this
// function is not strictly necessary as the file descriptor will be cleaned up
// by the garbage collector, but some long lived applications may not want to
// depend on that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight nit, but because the result of the comment has punctuation can you add a period on the end to align with the rest of the package comments?

@virtuald virtuald force-pushed the explicit-close branch 4 times, most recently from 7c77bdd to 9823d7f Compare August 13, 2018 19:10
@virtuald
Copy link
Contributor Author

Changed to return any error from Close.

@virtuald
Copy link
Contributor Author

Oh. Heh. I hadn't noticed that unlock also closes the file descriptor. Hm.

I suppose this isn't really necessary then -- though, I feel like I would really want to ensure that the descriptor gets closed, regardless of whether there's an error that occurs when unlocking?

Copy link
Member

@theckman theckman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely like the idea of this, if anything so that it implements io.Closer.

Would you be able to make these final tweaks, which I thinks should make it 💯?

flock_unix.go Outdated
@@ -85,7 +85,11 @@ func (f *Flock) lock(locked *bool, flag int) error {
func (f *Flock) Unlock() error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please move this function out to flock.go and remove it from flock_unix.go and flock_windows.go? With having the unexported unlockInternal method, we don't need the duplicated Unlock anymore.

flock.go Outdated
// function is not strictly necessary as the file descriptor will be cleaned up
// by the garbage collector, but some long lived applications may not want to
// depend on that.
func (f *Flock) Close() error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this function, but maybe just have it invoke return f.Unlock()

@theckman
Copy link
Member

@virtuald might you have a spare moment to look at this one more time?

@virtuald
Copy link
Contributor Author

I can take a look this weekend.

@virtuald
Copy link
Contributor Author

Since Unlock already does what I wanted, I took your suggestion and just added a close function that calls Unlock.

@theckman theckman merged commit 37b0e02 into gofrs:master Sep 15, 2018
theckman added a commit that referenced this pull request Sep 15, 2018
Add an explicit Close function

Signed-off-by: Tim Heckman <t@heckman.io>
@virtuald virtuald deleted the explicit-close branch September 17, 2018 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants