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

failpoint delete may block on "defer Release" #14

Closed
gyuho opened this issue Apr 24, 2018 · 5 comments
Closed

failpoint delete may block on "defer Release" #14

gyuho opened this issue Apr 24, 2018 · 5 comments

Comments

@gyuho
Copy link
Contributor

gyuho commented Apr 24, 2018

If failpoint is injected within for-loop, lock may never be released. And delete call would block.

#13 (comment)

OK, defer is broken in general. e.g., this will starve the delete too:

for ctx.Err() == nil {
  // gofail: var abc struct{}
  // fmt.Println("abc")
  whatever()
}
@gyuho
Copy link
Contributor Author

gyuho commented Apr 25, 2018

@heyitsanthony How about removing Release completely, since terms have been already evaluated by the time Acquire returns? Is there any other case that I am missing?

Before

raftDropHeartbeatLabel: /* gofail-label */
	for {
		m, err := dec.decode()
		if err != nil {
			cr.mu.Lock()
			cr.close()
			cr.mu.Unlock()
			return err
		}

		if vraftDropHeartbeat, __fpErr := __fp_raftDropHeartbeat.Acquire(); __fpErr == nil {
			defer __fp_raftDropHeartbeat.Release()
			_, __fpTypeOK := vraftDropHeartbeat.(struct{})
			if !__fpTypeOK {
				goto __badTyperaftDropHeartbeat
			}
			continue raftDropHeartbeatLabel
		__badTyperaftDropHeartbeat:
			__fp_raftDropHeartbeat.BadType(vraftDropHeartbeat, "struct{}")
		}

After

raftDropHeartbeatLabel: /* gofail-label */
	for {
		m, err := dec.decode()
		if err != nil {
			cr.mu.Lock()
			cr.close()
			cr.mu.Unlock()
			return err
		}

		if vraftDropHeartbeat, __fpErr := __fp_raftDropHeartbeat.Acquire(); __fpErr == nil {
-			defer __fp_raftDropHeartbeat.Release()
			_, __fpTypeOK := vraftDropHeartbeat.(struct{})
			if !__fpTypeOK {
				goto __badTyperaftDropHeartbeat
			}
			continue raftDropHeartbeatLabel
		__badTyperaftDropHeartbeat:
			__fp_raftDropHeartbeat.BadType(vraftDropHeartbeat, "struct{}")
		}

@heyitsanthony
Copy link
Contributor

heyitsanthony commented Apr 25, 2018

@gyuho it's important for current delete semantics:

// gofail: var crash struct{}
// panic("crash")

if the lock isn't held for the duration of the fp, then it's possible an inflight fp will crash the program after the delete is ack'd. One option is to have something like // gofail-go: to indicate that a delete will prevent new fp's from being issued, but has no control over whether any are inflight. This would also get around delete starvation for fp's that wait:

func a() {
    ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
    go f(ctx)
}

func f(ctx context.Context) {
// gofail-go: var waitForDone struct{}
// <-ctx.Done()
...
}

@ahrtr
Copy link
Member

ahrtr commented Nov 24, 2022

Eventually I removed the lock for each failpoint.

When the server(runtime) processes a http request, it acquires the global write lock. This prevents all failpoints from being triggered. It ensures the server(runtime) doesn't panic due to any failpoints during processing the HTTP request.

It may be inefficient, but correctness is more important than efficiency. Usually users will not enable too many failpoints at a
time, so it (the efficiency) isn't a problem.

#38

@serathius
Copy link
Member

Fully agree with the decision.

@ahrtr
Copy link
Member

ahrtr commented Nov 25, 2022

Resolved in #38

@ahrtr ahrtr closed this as completed Nov 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants