On Fri, Mar 07, 2014 at 11:05:28AM -0700, Eric Blake wrote:
On 03/03/2014 12:18 PM, Daniel P. Berrange wrote:
> Both
> of these options have a notable performance impact, however, and
> it is believed that worst case behaviour where the fields are
> read concurrently with being written would merely result in an
> mistaken emissions or dropping of the log message in question.
> This is an acceptable tradeoff for the performance benefit of
> avoiding locking.
>
Almost. As long as writes are safe, the worst that can happen is we
fail to emit a message that just got enabled, or we emit a message that
just got disabled. But had we used locks to avoid this race, and the
locks get obtained in reverse order, we would see the same behavior. So
the locks add no protection, and eliding them in favor of simpler
non-atomic integer ops is a safe action.
If there were only a single writer, then writes would be automatically
safe. However, you have multiple writers. Thus, you have the situation
that if two writers both try to increment the global serial with no
locks or atomic increments, you could end up with the classic symptoms
of over- or under-incrementing the global serial. If three threads compete:
start: global is 1
thread one: read global to compute its increment
thread two: read global to compute its increment
thread two: write the increment, global is now 2
thread three: compare local 0 against global 2, recompute priority
thread one: write the increment, global is now 2
thread three: compare local 2 against global 2, no priority recompute
Oops - if thread one changed priority so that thread three should no
longer log, we've messed up, and may have LOTS of messages logged that
should not have been, rather than just one or two at the race boundary
case. So I think your _increments_ need to be atomic, to protect the
writers, but those are not the hot path; while the READS of the global
can remain unprotected, and those are the actual hot path you are
optimizing in this patch.
The writes are already protected by a mutex so I don't think we need
to use atomic ops for increment either.
> static virLogFilterPtr virLogFilters = NULL;
> static int virLogNbFilters = 0;
>
> @@ -514,6 +515,7 @@ virLogResetFilters(void)
> VIR_FREE(virLogFilters[i].match);
> VIR_FREE(virLogFilters);
> virLogNbFilters = 0;
> + virLogFiltersSerial++;
> return i;
> }
>
> @@ -569,6 +571,7 @@ virLogDefineFilter(const char *match,
> virLogFilters[i].priority = priority;
> virLogFilters[i].flags = flags;
> virLogNbFilters++;
> + virLogFiltersSerial++;
These are the increments that I think are not hotpath, and need to be
serialized with an atomic increment.
These two functions run with the global log mutex held, so there can
only be one writer at a time.
>
> +static void
> +virLogSourceUpdate(virLogSourcePtr source)
> +{
> + virLogLock();
The common case is that the per-log serial will match the global serial;
while holding the lock on the boundary case of an updated global serial
is not going to affect hot case.
> /*
> - * check against list of specific logging patterns
> + * 3 intentionally non-thread safe variable reads.
> + * Worst case result is a log message is accidentally
> + * dropped or emitted, if another thread is updating
> + * log filter list concurrently with a log message
and again, if we WERE thread-safe, the race could have always been won
the other way in obtaining the lock, for the same end result, so our
lack of thread-safety isn't critical.
> */
> - fprio = virLogFiltersCheck(filename, &filterflags);
> - if (fprio == 0) {
> - if (priority < virLogDefaultPriority)
> - emit = false;
> - } else if (priority < fprio) {
> - emit = false;
> - }
> -
> - if (!emit)
> + if (source->serial < virLogFiltersSerial)
> + virLogSourceUpdate(source);
> + if (priority < source->priority)
> goto cleanup;
> + filterflags = source->flags;
Makes sense.
Regards,
Daniel
--
|:
http://berrange.com -o-
http://www.flickr.com/photos/dberrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|:
http://entangle-photo.org -o-
http://live.gnome.org/gtk-vnc :|