deadline.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Package deadline provides deadline timer used to implement
  2. // net.Conn compatible connection
  3. package deadline
  4. import (
  5. "context"
  6. "sync"
  7. "time"
  8. )
  9. // Deadline signals updatable deadline timer.
  10. // Also, it implements context.Context.
  11. type Deadline struct {
  12. exceeded chan struct{}
  13. stop chan struct{}
  14. stopped chan bool
  15. deadline time.Time
  16. mu sync.RWMutex
  17. }
  18. // New creates new deadline timer.
  19. func New() *Deadline {
  20. d := &Deadline{
  21. exceeded: make(chan struct{}),
  22. stop: make(chan struct{}),
  23. stopped: make(chan bool, 1),
  24. }
  25. d.stopped <- true
  26. return d
  27. }
  28. // Set new deadline. Zero value means no deadline.
  29. func (d *Deadline) Set(t time.Time) {
  30. d.mu.Lock()
  31. defer d.mu.Unlock()
  32. d.deadline = t
  33. close(d.stop)
  34. select {
  35. case <-d.exceeded:
  36. d.exceeded = make(chan struct{})
  37. default:
  38. stopped := <-d.stopped
  39. if !stopped {
  40. d.exceeded = make(chan struct{})
  41. }
  42. }
  43. d.stop = make(chan struct{})
  44. d.stopped = make(chan bool, 1)
  45. if t.IsZero() {
  46. d.stopped <- true
  47. return
  48. }
  49. if dur := time.Until(t); dur > 0 {
  50. exceeded := d.exceeded
  51. stopped := d.stopped
  52. go func() {
  53. timer := time.NewTimer(dur)
  54. select {
  55. case <-timer.C:
  56. close(exceeded)
  57. stopped <- false
  58. case <-d.stop:
  59. if !timer.Stop() {
  60. <-timer.C
  61. }
  62. stopped <- true
  63. }
  64. }()
  65. return
  66. }
  67. close(d.exceeded)
  68. d.stopped <- false
  69. }
  70. // Done receives deadline signal.
  71. func (d *Deadline) Done() <-chan struct{} {
  72. d.mu.RLock()
  73. defer d.mu.RUnlock()
  74. return d.exceeded
  75. }
  76. // Err returns context.DeadlineExceeded if the deadline is exceeded.
  77. // Otherwise, it returns nil.
  78. func (d *Deadline) Err() error {
  79. d.mu.RLock()
  80. defer d.mu.RUnlock()
  81. select {
  82. case <-d.exceeded:
  83. return context.DeadlineExceeded
  84. default:
  85. return nil
  86. }
  87. }
  88. // Deadline returns current deadline.
  89. func (d *Deadline) Deadline() (time.Time, bool) {
  90. d.mu.RLock()
  91. defer d.mu.RUnlock()
  92. if d.deadline.IsZero() {
  93. return d.deadline, false
  94. }
  95. return d.deadline, true
  96. }
  97. // Value returns nil.
  98. func (d *Deadline) Value(interface{}) interface{} {
  99. return nil
  100. }