Search code examples
restgogoroutine

Temporary lock the resource until goroutine is finished


I have a method, which sends HTTP status-code 202 Accepted as indicator of successful request, and runs another process in it (goroutine). Something like that:

return func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusAccepted)
    go func() {
        time.Sleep(2 * time.Second)
    }()
}

I want to temporarily lock the resource to prevent multiple execution of goroutine process.

return func(w http.ResponseWriter, r *http.Request) {
    c := make(chan bool)

    select {
    case _, unlocked := <-c:
        if unlocked {
            break
        }
    default:
        w.WriteHeader(http.StatusLocked)
        return
    }

    w.WriteHeader(http.StatusAccepted)
    go func(c chan bool) {
        time.Sleep(2 * time.Second)
        c <- true
    }(c)
}

I'm get 423 Locked status code always. I think, I don't understand channel yet. May be try to use mutexes?


Solution

  • Not the best solution, but it works:

    func (h *HookHandler) NewEvents() http.HandlerFunc {
        eventsLocker := struct {
            v   bool
            mux *sync.Mutex
        }{
            v:   true,
            mux: &sync.Mutex{},
        }
    
        return func(w http.ResponseWriter, r *http.Request) {
            if !eventsLocker.v {
                w.WriteHeader(http.StatusLocked)
                return
            }
    
            w.WriteHeader(http.StatusAccepted)
    
            go func() {
                defer eventsLocker.mux.Unlock()
                defer func() { eventsLocker.v = true }()
    
                eventsLocker.mux.Lock()
                eventsLocker.v = false
    
                time.Sleep(2 * time.Second)
            }()
        }
    }