fixed up use of context to stop loops by using a select with case <-ctx.Done()

This commit is contained in:
2026-02-22 21:59:36 -07:00
parent 1c6082324e
commit 43c34c9ffc
2 changed files with 161 additions and 157 deletions
+15 -12
View File
@@ -101,19 +101,22 @@ func AmDeliverSubscription(ctx context.Context, comm *database.Community, conf *
// The delivery loop; build each message and send it. Note that sending a message puts the Message structure on
// the sender goroutine channel, so we have to create a new Message each time, unlike what we did in Venice.
SendLoop:
for i := range recipUids {
err = ctx.Err()
if err != nil {
log.Errorf("AmDeliverSubscription: aborted on send loop iter %d with %v", i+1, err)
}
if ci, err := database.AmGetContactInfoForUser(ctx, recipUids[i]); err == nil {
msg := AmNewEmailMessage(poster.Uid, ipaddr)
msg.SetSubject(subjectSink.GetSubject())
msg.SetText(sendText)
msg.AddTo(*ci.Email, ci.FullName(false))
msg.Send()
} else {
log.Warnf("AmDeliverSubscription skipped uid %d because no contact info retrieved (%v)", recipUids[i], err)
select {
case <-ctx.Done():
log.Errorf("AmDeliverSubscription: aborted on send loop iter %d with %v", i+1, ctx.Err())
break SendLoop
default:
if ci, err := database.AmGetContactInfoForUser(ctx, recipUids[i]); err == nil {
msg := AmNewEmailMessage(poster.Uid, ipaddr)
msg.SetSubject(subjectSink.GetSubject())
msg.SetText(sendText)
msg.AddTo(*ci.Email, ci.FullName(false))
msg.Send()
} else {
log.Warnf("AmDeliverSubscription skipped uid %d because no contact info retrieved (%v)", recipUids[i], err)
}
}
}
}
+146 -145
View File
@@ -801,168 +801,169 @@ func (ht *htmlCheckerImpl) finishParen() error {
func (ht *htmlCheckerImpl) parse(str string) error {
i := 0
for i < len(str) {
err := ht.ctx.Err()
if err != nil {
return err
}
ch := str[i]
switch ht.state {
case stateWhitespace:
switch ch {
case ' ', '\t': // append space and tab verbatim
ht.tempBuffer.WriteByte(ch)
i++
case '\r', '\n': // flush and go to Newline state
ht.doFlushWhitespace()
ht.state = stateNewline
ht.tempBuffer.WriteByte(ch)
i++
case '<':
ht.doFlushWhitespace()
if ht.config.Angles {
ht.state = stateLeftAngle
} else {
// process < as ordinary character
select {
case <-ht.ctx.Done():
return ht.ctx.Err()
default:
ch := str[i]
switch ht.state {
case stateWhitespace:
switch ch {
case ' ', '\t': // append space and tab verbatim
ht.tempBuffer.WriteByte(ch)
i++
case '\r', '\n': // flush and go to Newline state
ht.doFlushWhitespace()
ht.state = stateNewline
ht.tempBuffer.WriteByte(ch)
i++
case '<':
ht.doFlushWhitespace()
if ht.config.Angles {
ht.state = stateLeftAngle
} else {
// process < as ordinary character
ht.state = stateChars
ht.tempBuffer.WriteByte(ch)
}
i++
case '(':
ht.doFlushWhitespace()
if ht.config.Parens {
ht.state = stateParen
} else {
// process ( as ordinary character)
ht.state = stateChars
ht.tempBuffer.WriteByte(ch)
}
i++
case '\\': // backslash processing is tricky - go to Chars state to handle it
ht.doFlushWhitespace()
ht.state = stateChars
default:
ht.doFlushWhitespace()
ht.state = stateChars
ht.tempBuffer.WriteByte(ch)
i++
}
i++
case '(':
ht.doFlushWhitespace()
if ht.config.Parens {
ht.state = stateParen
} else {
// process ( as ordinary character)
ht.state = stateChars
ht.tempBuffer.WriteByte(ch)
}
i++
case '\\': // backslash processing is tricky - go to Chars state to handle it
ht.doFlushWhitespace()
ht.state = stateChars
default:
ht.doFlushWhitespace()
ht.state = stateChars
ht.tempBuffer.WriteByte(ch)
i++
}
case stateChars:
switch ch {
case ' ', '\t': // go to Whitespace state
_, err := ht.doFlushString()
if err != nil {
return err
}
ht.state = stateWhitespace
ht.tempBuffer.WriteByte(ch)
i++
case '\r', '\n': // go to Newline state
_, err := ht.doFlushString()
if err != nil {
return err
}
ht.state = stateNewline
ht.tempBuffer.WriteByte(ch)
i++
case '<': // may be a start of tag
if ht.config.Angles {
case stateChars:
switch ch {
case ' ', '\t': // go to Whitespace state
_, err := ht.doFlushString()
if err != nil {
return err
}
ht.state = stateLeftAngle
} else {
ht.tempBuffer.WriteByte(ch)
}
i++
case '\\':
if i < (len(str) - 1) {
i++
ch = str[i]
if (ch == '(' && ht.config.Parens) || (ch == '<' && ht.config.Angles) {
// append the escaped character, omitting the backslash
ht.tempBuffer.WriteByte(ch)
i++
} else {
// append the backslash and hit the new character
ht.tempBuffer.WriteByte('\\')
}
} else {
// just append the backslash normally
ht.state = stateWhitespace
ht.tempBuffer.WriteByte(ch)
i++
}
default: // just append the next character
ht.tempBuffer.WriteByte(ch)
i++
}
case stateLeftAngle:
switch ch {
case ' ', '\t', '\r', '\n': // output <, go to Whitespace state
ht.emitRune('<', ht.outputFilters, true)
ht.state = stateWhitespace
case '<': // output < and stay in this state
ht.emitRune('<', ht.outputFilters, true)
i++
default: // begin processing tag
ht.state = stateTag
ht.tempBuffer.WriteByte(ch)
i++
}
case stateTag:
switch ch {
case '>': // finish the tag - this changes the state, and possibly calls parse() recursively
err := ht.finishTag()
if err != nil {
return err
}
i++
case '\'', '"': // go into "quote string" state inside the tag
ht.tempBuffer.WriteByte(ch)
ht.state = stateTagQuote
ht.quoteChar = ch
i++
default: // just append the character
ht.tempBuffer.WriteByte(ch)
i++
}
case stateParen:
switch ch {
case '(': // nest parentheses one level deeper
ht.tempBuffer.WriteByte(ch)
ht.parenLevel++
i++
case ')':
if ht.parenLevel == 0 {
err := ht.finishParen() // finish paren, changing state and recursively parsing if necessary
case '\r', '\n': // go to Newline state
_, err := ht.doFlushString()
if err != nil {
return err
}
} else {
// nest parentheses one LESS level deeper
ht.state = stateNewline
ht.tempBuffer.WriteByte(ch)
ht.parenLevel--
i++
case '<': // may be a start of tag
if ht.config.Angles {
_, err := ht.doFlushString()
if err != nil {
return err
}
ht.state = stateLeftAngle
} else {
ht.tempBuffer.WriteByte(ch)
}
i++
case '\\':
if i < (len(str) - 1) {
i++
ch = str[i]
if (ch == '(' && ht.config.Parens) || (ch == '<' && ht.config.Angles) {
// append the escaped character, omitting the backslash
ht.tempBuffer.WriteByte(ch)
i++
} else {
// append the backslash and hit the new character
ht.tempBuffer.WriteByte('\\')
}
} else {
// just append the backslash normally
ht.tempBuffer.WriteByte(ch)
i++
}
default: // just append the next character
ht.tempBuffer.WriteByte(ch)
i++
}
case stateLeftAngle:
switch ch {
case ' ', '\t', '\r', '\n': // output <, go to Whitespace state
ht.emitRune('<', ht.outputFilters, true)
ht.state = stateWhitespace
case '<': // output < and stay in this state
ht.emitRune('<', ht.outputFilters, true)
i++
default: // begin processing tag
ht.state = stateTag
ht.tempBuffer.WriteByte(ch)
i++
}
case stateTag:
switch ch {
case '>': // finish the tag - this changes the state, and possibly calls parse() recursively
err := ht.finishTag()
if err != nil {
return err
}
i++
case '\'', '"': // go into "quote string" state inside the tag
ht.tempBuffer.WriteByte(ch)
ht.state = stateTagQuote
ht.quoteChar = ch
i++
default: // just append the character
ht.tempBuffer.WriteByte(ch)
i++
}
case stateParen:
switch ch {
case '(': // nest parentheses one level deeper
ht.tempBuffer.WriteByte(ch)
ht.parenLevel++
i++
case ')':
if ht.parenLevel == 0 {
err := ht.finishParen() // finish paren, changing state and recursively parsing if necessary
if err != nil {
return err
}
} else {
// nest parentheses one LESS level deeper
ht.tempBuffer.WriteByte(ch)
ht.parenLevel--
}
i++
default:
ht.tempBuffer.WriteByte(ch)
i++
}
case stateTagQuote:
ht.tempBuffer.WriteByte(ch)
if ch == ht.quoteChar {
ht.state = stateTag
}
i++
case stateNewline:
if ch == '\r' || ch == '\n' {
ht.tempBuffer.WriteByte(ch)
i++
} else {
ht.doFlushNewlines()
}
default:
ht.tempBuffer.WriteByte(ch)
i++
log.Fatalf("invalid parser state: %d", ht.state)
}
case stateTagQuote:
ht.tempBuffer.WriteByte(ch)
if ch == ht.quoteChar {
ht.state = stateTag
}
i++
case stateNewline:
if ch == '\r' || ch == '\n' {
ht.tempBuffer.WriteByte(ch)
i++
} else {
ht.doFlushNewlines()
}
default:
log.Fatalf("invalid parser state: %d", ht.state)
}
}
return nil