mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 09:52:26 -06:00 
			
		
		
		
	
		
			
	
	
		
			76 lines
		
	
	
	
		
			1.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			76 lines
		
	
	
	
		
			1.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package runners
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Processable defines a runnable process with error return
							 | 
						||
| 
								 | 
							
								// that can be passed to a Processor instance for managed running.
							 | 
						||
| 
								 | 
							
								type Processable func() error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Processor acts similarly to a sync.Once object, except that it is reusable. After
							 | 
						||
| 
								 | 
							
								// the first call to Process(), any further calls before this first has returned will
							 | 
						||
| 
								 | 
							
								// block until the first call has returned, and return the same error. This ensures
							 | 
						||
| 
								 | 
							
								// that only a single instance of it is ever running at any one time.
							 | 
						||
| 
								 | 
							
								type Processor struct {
							 | 
						||
| 
								 | 
							
									mutex sync.Mutex
							 | 
						||
| 
								 | 
							
									state uint32
							 | 
						||
| 
								 | 
							
									wait  sync.WaitGroup
							 | 
						||
| 
								 | 
							
									err   *error
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Process will process the given function if first-call, else blocking until
							 | 
						||
| 
								 | 
							
								// the first function has returned, returning the same error result.
							 | 
						||
| 
								 | 
							
								func (p *Processor) Process(proc Processable) (err error) {
							 | 
						||
| 
								 | 
							
									// Acquire state lock.
							 | 
						||
| 
								 | 
							
									p.mutex.Lock()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if p.state != 0 {
							 | 
						||
| 
								 | 
							
										// Already running.
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
										// Get current err ptr.
							 | 
						||
| 
								 | 
							
										errPtr := p.err
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Wait until finish.
							 | 
						||
| 
								 | 
							
										p.mutex.Unlock()
							 | 
						||
| 
								 | 
							
										p.wait.Wait()
							 | 
						||
| 
								 | 
							
										return *errPtr
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Reset error ptr.
							 | 
						||
| 
								 | 
							
									p.err = new(error)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Set started.
							 | 
						||
| 
								 | 
							
									p.wait.Add(1)
							 | 
						||
| 
								 | 
							
									p.state = 1
							 | 
						||
| 
								 | 
							
									p.mutex.Unlock()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if r := recover(); r != nil {
							 | 
						||
| 
								 | 
							
											if err != nil {
							 | 
						||
| 
								 | 
							
												rOld := r // wrap the panic so we don't lose existing returned error
							 | 
						||
| 
								 | 
							
												r = fmt.Errorf("panic occured after error %q: %v", err.Error(), rOld)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// Catch any panics and wrap as error.
							 | 
						||
| 
								 | 
							
											err = fmt.Errorf("caught panic: %v", r)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Store error.
							 | 
						||
| 
								 | 
							
										*p.err = err
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Mark done.
							 | 
						||
| 
								 | 
							
										p.wait.Done()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Set stopped.
							 | 
						||
| 
								 | 
							
										p.mutex.Lock()
							 | 
						||
| 
								 | 
							
										p.state = 0
							 | 
						||
| 
								 | 
							
										p.mutex.Unlock()
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Run process.
							 | 
						||
| 
								 | 
							
									err = proc()
							 | 
						||
| 
								 | 
							
									return
							 | 
						||
| 
								 | 
							
								}
							 |