Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

threads and log

129 views
Skip to first unread message

Darkbyte

unread,
Feb 28, 2009, 4:36:05 AM2/28/09
to
Hello everybody!
I have a multi-thread project, in which I'd like to add a function
addlog(char *text).
The function should open a file, write the text, close the file.
Question: if the execution switch from a thread to another when the
file has not been closed yet, what can be written?
Maybe I'll read something like two merged text?
I should use the addlog() in many points of the threads functions, so
a down() or up() in the addlog() could make slow the entire execution,
or am I wrong?
How can I log in the same file without problems?
I also could write more logs, one-per-thread, but find a bug reading
more files will be hard...

Thanx!!!

David Schwartz

unread,
Feb 28, 2009, 2:29:33 PM2/28/09
to

Why close the file in the function if the function is a possible
performance bottleneck?

DS

Marcel Müller

unread,
Feb 28, 2009, 7:01:06 PM2/28/09
to
Darkbyte wrote:
> I have a multi-thread project, in which I'd like to add a function
> addlog(char *text).
> The function should open a file, write the text, close the file.

Bad idea.

> Question: if the execution switch from a thread to another when the
> file has not been closed yet, what can be written?

That depends on your open mode. But whatever happens it is very likely
not that what you want, because appending to a file is not atomic in
most cases and operating systems.

> Maybe I'll read something like two merged text?

Maybe.

> I should use the addlog() in many points of the threads functions, so
> a down() or up() in the addlog() could make slow the entire execution,
> or am I wrong?

Whatever up() and down() is.

> How can I log in the same file without problems?

Either use a mutex or use the same stream handle for all threads and
write with a single runtime call. normally calls to the i/o functions of
a single stream are serialized by the runtime anyway.

> I also could write more logs, one-per-thread, but find a bug reading
> more files will be hard...

Yes.

In fact you should always write
- a time stamp,
- the current thread id and
- the stack pointer
in addlog. Otherwise it will be difficult to interpret the log.

If not otherwise needed you may simply log to stderr which you can
redirect to a file when starting your application.


Marcel

Chris M. Thomasson

unread,
Feb 28, 2009, 7:34:07 PM2/28/09
to
"Darkbyte" <seprano...@gmail.com> wrote in message
news:9498d41c-2bca-4c06...@c11g2000yqj.googlegroups.com...

I did a logging system that went something like this:


1. a dedicated logging thread that was comprised of a list of registered
application threads.

2. a plurality of application threads that have a private single
producer/consumer queue per-thread.

3. a log entry comprised of a timestamp, thread id, importance, status,
ect...

4. log entries are enqueued by a thread into its private queue.

5. dedicated logging thread periodically samples each of registered
application threads private queues.

6. log entries are dequeued, sorted, and written to a file by the single
dedicated log thread.

This logging scheme scales, and does not even need any mutexs on the single
producer/consumer queues.


It worked very well.

Marcel Müller

unread,
Feb 28, 2009, 7:41:23 PM2/28/09
to
Hi,

Chris M. Thomasson wrote:
[...]


> This logging scheme scales, and does not even need any mutexs on the
> single producer/consumer queues.

not even for the allocation of storage for the data that is logged?
I wonder a bit who owns the storage while the log items are in the queue.

Another disadvantage might be that the last log messages are lost in
case of an application crash. OK, a signal handler might recover from this.


Marcel

Chris M. Thomasson

unread,
Feb 28, 2009, 8:47:54 PM2/28/09
to
"Marcel Müller" <news.5...@spamgourmet.com> wrote in message
news:49a9d9b4$0$31871$9b4e...@newsspool3.arcor-online.net...

> Hi,
>
> Chris M. Thomasson wrote:
> [...]
>> This logging scheme scales, and does not even need any mutexs on the
>> single producer/consumer queues.
>
> not even for the allocation of storage for the data that is logged?

It really depends on the implementation of the underlying allocator. In the
particular scheme I created, log messages were finite in length, so a thread
pre-allocated them in bulk at birth; the act of dequeuing a pre-allocated
buffer is very cheap.


> I wonder a bit who owns the storage while the log items are in the queue.

Once a log buffer was queued, it belongs to the dedicated logger thread; it
was a simple transfer of ownership. The logging thread was responsible for
freeing buffers. Also, I simply failed to mention that when a thread ended
its lifetime, its TSD dtor fired up which transferred all remaining log
entries to the main log thread via a multiple producer single consumer
queue.


> Another disadvantage might be that the last log messages are lost in case
> of an application crash.

If an application crashes, well, AFAICT its undefined exactly where things
stop working. So, IMVHO, an application crash can adversely effect other
logging schemes as well.


> OK, a signal handler might recover from this.

Indeed.

Dmitriy Vyukov

unread,
Mar 1, 2009, 2:19:13 AM3/1/09
to
On Mar 1, 3:41 am, Marcel Müller <news.5.ma...@spamgourmet.com> wrote:
> Hi,
>
> Chris M. Thomasson wrote:
>
> [...]
>
> > This logging scheme scales, and does not even need any mutexs on the
> > single producer/consumer queues.
>
> not even for the allocation of storage for the data that is logged?

One may use following scheme to manage memory (atomic-free too):
http://groups.google.com/group/comp.programming.threads/browse_frm/thread/378a35b21ae2b42e/

Although it requires per-producer buffering of memory, so it may cause
excessive memory consumption. So one better bound number of per-
producer memory blocks, and use something like spin-wait if all memory
blocks are busy. Otherwise one can run into following very bad
situation:
http://blogtrader.net/page/dcaoyuan/entry/a_case_study_of_scalable


> I wonder a bit who owns the storage while the log items are in the queue.

Consumer (log thread).


> Another disadvantage might be that the last log messages are lost in
> case of an application crash. OK, a signal handler might recover from this.

Another solution is to allocate log buffers in shared memory.

--
Dmitriy V'jukov

Dmitriy Vyukov

unread,
Mar 1, 2009, 2:54:14 AM3/1/09
to
On Mar 1, 3:34 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:

> This logging scheme scales, and does not even need any mutexs on the single
> producer/consumer queues.

I am only a bit concerned with the fact that large amount amounts of
memory are constantly transferred between the threads. So this may
cause a large cache-coherence overheads, which may be significantly
larger than mutex acquisition. What do you think about this? Or logger
thread is not touching the memory and just simply offloads it to the
OS for writing to the file?

--
Dmitriy V'jukov

Chris M. Thomasson

unread,
Mar 1, 2009, 5:29:16 AM3/1/09
to
"Dmitriy Vyukov" <dvy...@gmail.com> wrote in message
news:5eb38dc5-4b27-4a60...@z9g2000yqi.googlegroups.com...

On Mar 1, 3:34 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:

> > This logging scheme scales, and does not even need any mutexs on the
> > single
> > producer/consumer queues.

> I am only a bit concerned with the fact that large amount amounts of
> memory are constantly transferred between the threads. So this may
> cause a large cache-coherence overheads, which may be significantly
> larger than mutex acquisition. What do you think about this?

I did not consider cache-coherence overheads in this case. It was a long
time ago. I cannot remember results of performance testing wrt per-thread
queue vs. global queue. IIRC, log entries were frequent in some cases; I did
not want to take a lock for each one.


> Or logger
> thread is not touching the memory and just simply offloads it to the
> OS for writing to the file?

The logger thread did do some sorting on log entry timestamps and thread id;
write to file; free log entry... IIRC, it only mutated the embedded double
linked list nodes during the sorting phase.

Chris M. Thomasson

unread,
Mar 1, 2009, 5:47:22 AM3/1/09
to
"Dmitriy Vyukov" <dvy...@gmail.com> wrote in message
news:53847514-010a-4d0f...@w35g2000yqm.googlegroups.com...

On Mar 1, 3:41 am, Marcel Müller <news.5.ma...@spamgourmet.com> wrote:
> > Hi,
> >
> > Chris M. Thomasson wrote:
> >
> > [...]
> >
> > > This logging scheme scales, and does not even need any mutexs on the
> > > single producer/consumer queues.
> >
> > not even for the allocation of storage for the data that is logged?

> One may use following scheme to manage memory (atomic-free too):
> http://groups.google.com/group/comp.programming.threads/browse_frm/thread/378a35b21ae2b42e/

> Although it requires per-producer buffering of memory, so it may cause
> excessive memory consumption. So one better bound number of per-
> producer memory blocks, and use something like spin-wait if all memory
> blocks are busy. Otherwise one can run into following very bad
> situation:
> http://blogtrader.net/page/dcaoyuan/entry/a_case_study_of_scalable

IIRC, the logging scheme I created could run into similar scenario.
Although, it never was a problem wrt the applications which used it.
However, I think it could be solved by using a per-thread fast-pathed
semaphore, or a single global semaphore.


Something like:


struct log_entry {
struct log_entry* next;
struct log_entry* prev;
// [...]
};


struct thread {
dlist link;
semaphore log_max; // initial value to 1000, or something.
struct log_entry* log_freelist;
spsc_queue log_queue;
};


struct log_thread {
dlist threads;
event ec;
};


static struct log_thread g_logger;


void thread_log(struct thread* self, ...) {
if (! self->log_max.try_wait()) {
g_logger.ec.signal();
self->log_max.wait();
}
// enqueue
}


void log_thread() {
for (;;) {
g_logger.ec.timed_wait(poll_interval);
for_each_thread(struct thread* thread) {
unsigned count = 0;
for_each_log_entry_in_thread(struct log_entry* log) {
// [...];
++count;
}
thread->log_max.post(count);
}
}
}


> [...]

0 new messages