Fix race condition surrounding global orgWriter

Since writers are normally only used synchronously (i.e. to write one document
at a time), we don't guard modifications to their internal
state (e.g. temporarily replacing the string.Builder in WriteNodesAsString)
against race conditions.

The package global `orgWriter` and corresponding use cases of it (`org.String`,
`$node.String`) break that pattern - the writer is potentially used from
multiple go routines at the same time. This results in race conditions that
manifest as error messages like e.g.

    could not write output: runtime error: invalid memory address or nil pointer dereference. Using unrendered content.

Additionally, since we catch panics in `Document.Write`, the corresponding
stack trace is lost and dependents of go-org never know what hit them.

As using a writer across simultaneously across go routines is not a standard
pattern, we'll sync the use of the global `orgWriter` instead of trying to make
the actual writer threadsafe; less code noise for the common use case.
This commit is contained in:
Niklas Fasching 2023-03-12 11:08:19 +01:00
parent 18314a9f41
commit 5464ab37d2
12 changed files with 41 additions and 35 deletions

View file

@ -409,14 +409,14 @@ func (w *HTMLWriter) WriteRegularLink(l RegularLink) {
if l.Description == nil {
w.WriteString(fmt.Sprintf(`<img src="%s" alt="%s" title="%s" />`, url, url, url))
} else {
description := strings.TrimPrefix(String(l.Description), "file:")
description := strings.TrimPrefix(String(l.Description...), "file:")
w.WriteString(fmt.Sprintf(`<a href="%s"><img src="%s" alt="%s" /></a>`, url, description, description))
}
case "video":
if l.Description == nil {
w.WriteString(fmt.Sprintf(`<video src="%s" title="%s">%s</video>`, url, url, url))
} else {
description := strings.TrimPrefix(String(l.Description), "file:")
description := strings.TrimPrefix(String(l.Description...), "file:")
w.WriteString(fmt.Sprintf(`<a href="%s"><video src="%s" title="%s"></video></a>`, url, description, description))
}
default: