Skip to content

Commit

Permalink
Use Thread instance variables to handle XML errors
Browse files Browse the repository at this point in the history
Use instead of ThreadLocal variables which don't play well with the GC and they
are not supported in all platforms. Thanks @asterite, @RX14 and @ysbaddaden!
  • Loading branch information
ggiraldez committed Feb 24, 2017
1 parent 7d9f414 commit 4c2e8e0
Showing 1 changed file with 38 additions and 28 deletions.
66 changes: 38 additions & 28 deletions src/xml/error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,59 @@ class XML::Error < Exception
super(message)
end

@[ThreadLocal]
@@errors = [] of self
def self.init_thread_error_handling
Thread.current.xml_init_error_handling
end

@[ThreadLocal]
@@initialized = false
# :nodoc:
def self.set_errors(node)
if errors = self.errors
node.errors = errors
end
end

def self.init_thread_error_handling
return if @@initialized
def self.errors
xml_errors = Thread.current.xml_errors
if xml_errors.empty?
nil
else
errors = xml_errors.dup
xml_errors.clear
errors
end
end
end

class Thread
@__xml_errors = [] of XML::Error
@__xml_errors_initialized = false

LibXML.xmlSetStructuredErrorFunc nil, ->(ctx, error) {
@@errors << XML::Error.new(error)
def xml_errors
@__xml_errors
end

def xml_init_error_handling
return if @__xml_errors_initialized

LibXML.xmlSetStructuredErrorFunc self.as(Void*), ->(ctx, error) {
thread = ctx.as(Thread)
thread.xml_errors << XML::Error.new(error)
}

LibXML.xmlSetGenericErrorFunc nil, ->(ctx, fmt) {
LibXML.xmlSetGenericErrorFunc self.as(Void*), ->(ctx, fmt) {
thread = ctx.as(Thread)
# TODO: use va_start and va_end to
message = String.new(fmt).chomp
error = XML::Error.new(message, 0)

{% if flag?(:arm) || flag?(:aarch64) %}
# libxml2 is likely missing ARM unwind tables (.ARM.extab and .ARM.exidx
# sections) which prevent raising from a libxml2 context.
@@errors << error
thread.xml_errors << error
{% else %}
raise error
{% end %}
}
@@initialized = true
end

# :nodoc:
def self.set_errors(node)
if errors = self.errors
node.errors = errors
end
end

def self.errors
if @@errors.empty?
nil
else
errors = @@errors.dup
@@errors.clear
errors
end
@__xml_errors_initialized = true
end
end

0 comments on commit 4c2e8e0

Please sign in to comment.