public inbox for pthreads-win32@sourceware.org
 help / color / mirror / Atom feed
* Handle leak when using pthread mutex with win32 api threads
@ 2005-03-04 15:53 Dmitry Sernii
  2005-03-04 17:06 ` Gottlob Frege
  2005-03-04 17:07 ` Gottlob Frege
  0 siblings, 2 replies; 9+ messages in thread
From: Dmitry Sernii @ 2005-03-04 15:53 UTC (permalink / raw)
  To: pthreads-win32

Hello All!
I have handle leak, when using win32 api threads with pthread mutexes.
I'm using pthread-win32 2005-01-03 pthread snapshot.

Here is the sample application which reproduce the problem:

#include <pthread.h>
#include <windows.h>
#include <conio.h>

const int max_threads = 500;

DWORD WINAPI threadProc(void* param)
{
	static pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);	
	pthread_mutex_lock(&mutex);		
	pthread_mutex_unlock(&mutex);
	return 0;
}

void main()
{
	DWORD id;
	HANDLE th[max_threads];
	int i;
	for (i=0;i<max_threads;i++)
		th[i]=CreateThread(0,0,threadProc,0,0,&id);
	
	for (i=0;i<max_threads;i++)
	{
		WaitForSingleObject(th[i],INFINITE);
		CloseHandle(th[i]);
	}
	getch();
}

after that if you take a look at Task Manager you can see lots of
handlers used by the program. If you replace win32 API threads with
pthreads everything works fine. Also this program works ok if you
change pthread mutexes with win32 mutexes.
The same problem happends when using QT threads (version 3.3.4
)instead of win32 api threads.

problem reproduces with MSVC 6.0 compiler.

Best Regards,
Dmitrii Sernii

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
  2005-03-04 15:53 Handle leak when using pthread mutex with win32 api threads Dmitry Sernii
@ 2005-03-04 17:06 ` Gottlob Frege
  2005-03-04 22:27   ` Ross Johnson
  2005-03-04 17:07 ` Gottlob Frege
  1 sibling, 1 reply; 9+ messages in thread
From: Gottlob Frege @ 2005-03-04 17:06 UTC (permalink / raw)
  To: Dmitry Sernii; +Cc: pthreads-win32

On Fri, 4 Mar 2005 17:52:58 +0200, Dmitry Sernii <bogolt@gmail.com> wrote:
> Hello All!
> I have handle leak, when using win32 api threads with pthread mutexes.
> I'm using pthread-win32 2005-01-03 pthread snapshot.
> 
> Here is the sample application which reproduce the problem:
> 
> #include <pthread.h>
> #include <windows.h>
> #include <conio.h>
> 
> const int max_threads = 500;
> 
> DWORD WINAPI threadProc(void* param)
> {
>        static pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);

that line above is not thread safe.  you might initialize the same
mutex multiple times.  Not good.

>        pthread_mutex_lock(&mutex);
>        pthread_mutex_unlock(&mutex);
>        return 0;
> }
> 
> void main()
> {
>        DWORD id;
>        HANDLE th[max_threads];
>        int i;
>        for (i=0;i<max_threads;i++)
>                th[i]=CreateThread(0,0,threadProc,0,0,&id);
> 
>        for (i=0;i<max_threads;i++)
>        {
>                WaitForSingleObject(th[i],INFINITE);
>                CloseHandle(th[i]);
>        }
>        getch();
> }
> 
> after that if you take a look at Task Manager you can see lots of
> handlers used by the program. If you replace win32 API threads with
> pthreads everything works fine. Also this program works ok if you
> change pthread mutexes with win32 mutexes.
> The same problem happends when using QT threads (version 3.3.4
> )instead of win32 api threads.
> 
> problem reproduces with MSVC 6.0 compiler.
> 
> Best Regards,
> Dmitrii Sernii
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
  2005-03-04 15:53 Handle leak when using pthread mutex with win32 api threads Dmitry Sernii
  2005-03-04 17:06 ` Gottlob Frege
@ 2005-03-04 17:07 ` Gottlob Frege
  1 sibling, 0 replies; 9+ messages in thread
From: Gottlob Frege @ 2005-03-04 17:07 UTC (permalink / raw)
  To: Dmitry Sernii; +Cc: pthreads-win32

On Fri, 4 Mar 2005 17:52:58 +0200, Dmitry Sernii <bogolt@gmail.com> wrote:
> Hello All!
> I have handle leak, when using win32 api threads with pthread mutexes.
> I'm using pthread-win32 2005-01-03 pthread snapshot.
> 
> Here is the sample application which reproduce the problem:
> 
> #include <pthread.h>
> #include <windows.h>
> #include <conio.h>
> 
> const int max_threads = 500;
> 
> DWORD WINAPI threadProc(void* param)
> {
>        static pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);

that line above is not thread safe.  you might initialize the same
mutex multiple times.  (the C++ standard says it gets initted once,
but it doesn't talk about threads.  If you look at the disassembly, it
probably only guards the constructor with a static bool flag - not in
a thread safe way.) Not good.  In this case, putting the line OUTSIDE
the threadProc function would be good enough to make the mutex
constructor really only happen once.

>        pthread_mutex_lock(&mutex);
>        pthread_mutex_unlock(&mutex);
>        return 0;
> }
> 
> void main()
> {
>        DWORD id;
>        HANDLE th[max_threads];
>        int i;
>        for (i=0;i<max_threads;i++)
>                th[i]=CreateThread(0,0,threadProc,0,0,&id);
> 
>        for (i=0;i<max_threads;i++)
>        {
>                WaitForSingleObject(th[i],INFINITE);
>                CloseHandle(th[i]);
>        }
>        getch();
> }
> 
> after that if you take a look at Task Manager you can see lots of
> handlers used by the program. If you replace win32 API threads with
> pthreads everything works fine. Also this program works ok if you
> change pthread mutexes with win32 mutexes.
> The same problem happends when using QT threads (version 3.3.4
> )instead of win32 api threads.
> 
> problem reproduces with MSVC 6.0 compiler.
> 
> Best Regards,
> Dmitrii Sernii
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
  2005-03-04 17:06 ` Gottlob Frege
@ 2005-03-04 22:27   ` Ross Johnson
  0 siblings, 0 replies; 9+ messages in thread
From: Ross Johnson @ 2005-03-04 22:27 UTC (permalink / raw)
  To: Pthreads-Win32 list

On Fri, 2005-03-04 at 12:03 -0500, Gottlob Frege wrote:
> On Fri, 4 Mar 2005 17:52:58 +0200, Dmitry Sernii <bogolt@gmail.com> wrote:
> > Hello All!
> > I have handle leak, when using win32 api threads with pthread mutexes.
> > I'm using pthread-win32 2005-01-03 pthread snapshot.
> > 
> > Here is the sample application which reproduce the problem:
> > 
> > #include <pthread.h>
> > #include <windows.h>
> > #include <conio.h>
> > 
> > const int max_threads = 500;
> > 
> > DWORD WINAPI threadProc(void* param)
> > {
> >        static pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> 
> that line above is not thread safe.  you might initialize the same
> mutex multiple times.  Not good.
> 

The handle leak comes about because, in pthreads-win32, pthread_mutex_t
is only a pointer, and the actual mutex struct is malloced by the
library, in this case, the first time you call pthread_mutex_lock().
That is, the library just calls pthread_mutex_init() for you. Also, you
must always call pthread_mutex_destroy() to clean up, just the same as
if you had called pthread_mutex_init() yourself.

So, in your sample code, any destructor that gets called to clean up
'mutex' will only clean up the pointer, leaving the mutex struct and any
handles that were created for it behind.

POSIX is defined in C terms and you can't use C++ shortcuts like this
generally. They may work for some pthreads implementations, but that
will be accidental. You need to find a C++ wrapper library or write one
if you want to take advantage of C++ magic.

Regards.
Ross

> >        pthread_mutex_lock(&mutex);
> >        pthread_mutex_unlock(&mutex);
> >        return 0;
> > }
> > 
> > void main()
> > {
> >        DWORD id;
> >        HANDLE th[max_threads];
> >        int i;
> >        for (i=0;i<max_threads;i++)
> >                th[i]=CreateThread(0,0,threadProc,0,0,&id);
> > 
> >        for (i=0;i<max_threads;i++)
> >        {
> >                WaitForSingleObject(th[i],INFINITE);
> >                CloseHandle(th[i]);
> >        }
> >        getch();
> > }
> > 
> > after that if you take a look at Task Manager you can see lots of
> > handlers used by the program. If you replace win32 API threads with
> > pthreads everything works fine. Also this program works ok if you
> > change pthread mutexes with win32 mutexes.
> > The same problem happends when using QT threads (version 3.3.4
> > )instead of win32 api threads.
> > 
> > problem reproduces with MSVC 6.0 compiler.
> > 
> > Best Regards,
> > Dmitrii Sernii
> >
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
  2005-03-05 13:31 Dmitrii Sernii
  2005-03-05 21:41 ` Ross Johnson
@ 2005-03-08  1:22 ` Ross Johnson
  1 sibling, 0 replies; 9+ messages in thread
From: Ross Johnson @ 2005-03-08  1:22 UTC (permalink / raw)
  To: Pthreads-Win32 list

The problem has been found, fixed and tested, and was indeed a problem
in the library.

Each POSIX handle keeps a duplicate of the underlying Win32 thread's
handle. Deliberately, but mistakenly, this duplicate handle was not
being closed for implicitly created pthreads handles (those not created
via a call to pthread_create).

It will be in CVS soon, and a new snapshot will be available later
today.

Many thanks.
Ross

On Sat, 2005-03-05 at 15:31 +0200, Dmitrii Sernii wrote:
> > > >        static pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> > >
> > > that line above is not thread safe.  you might initialize the same
> > > mutex multiple times.  Not good.
> > >
> > 
> > The handle leak comes about because, in pthreads-win32, pthread_mutex_t
> > is only a pointer, and the actual mutex struct is malloced by the
> > library, in this case, the first time you call pthread_mutex_lock().
> > That is, the library just calls pthread_mutex_init() for you. Also, you
> > must always call pthread_mutex_destroy() to clean up, just the same as
> > if you had called pthread_mutex_init() yourself.
> 
> I've made small corrections in test sample, so now mutex initialised
> only once, and i've added pthread_mutex_destroy() function call. But
> the problem still remains.
> Here is two test applications - one of them uses API threads, and the
> other pthreads. Program with pthreads don't have any leaks, while
> program with API threads produces lots of them.
> 
> //program with handle leaks
> #include <pthread.h>
> #include <windows.h>
> #include <conio.h>
> 
> pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> 
> DWORD WINAPI threadProc(void *param)
> {
> 	pthread_mutex_lock(&mutex);
> 	pthread_mutex_unlock(&mutex);
> 	return 0;
> }
> 
> void main()
> {
> 	const int max_threads = 500;
> 	DWORD id;
> 	HANDLE th[max_threads];
> 	int i;
> 	for (i=0;i<max_threads;i++)
> 	th[i]=CreateThread(0,0,threadProc,0,0,&id);
> 
> 	for (i=0;i<max_threads;i++)
> 	{
> 		WaitForSingleObject(th[i],INFINITE);
> 		CloseHandle(th[i]);
> 	}
> 	pthread_mutex_destroy(&mutex);
> 	getch();
> }
> 
> ==========================
> 
> //program without handle leaks
> #include <pthread.h>
> #include <windows.h>
> #include <conio.h>
> 
> 
> pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> 
> void* threadProc(void *param)
> {
> 	pthread_mutex_lock(&mutex);
> 	pthread_mutex_unlock(&mutex);
>     return 0;
> }
> 
> void main()
> {
> 	const int max_threads = 500;
> 	DWORD id;
> 	pthread_t th[max_threads];
> 	int i;
> 
> 	for (i=0;i<max_threads;i++)
> 			pthread_create(&(th[i]),0,threadProc,(void*)i);
> 
> 	for (i=0;i<max_threads;i++)
> 		pthread_join(th[i],(void**)&id);
> 
> 	pthread_mutex_destroy(&mutex);
> 	getch();
> }
> 
> Best regards,
> Dmitrii Sernii
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: Handle leak when using pthread mutex with win32 api threads
@ 2005-03-06 12:51 Bossom, John
  0 siblings, 0 replies; 9+ messages in thread
From: Bossom, John @ 2005-03-06 12:51 UTC (permalink / raw)
  To: Dmitrii Sernii, pthreads-win32

My hands have been out of the pthreads-win32 code base for some time
(1998!)

However, I do know that on UNIX you can and will leak resources if
you do not treat your main line like any other thread. In otherwords,
call pthread_exit() to end your mainline. 

I believe this is documented in the book written by Bil Lewis.

Cheers,

John Bossom



-----Original Message-----
From: pthreads-win32-owner@sources.redhat.com
[mailto:pthreads-win32-owner@sources.redhat.com] On Behalf Of Dmitrii
Sernii
Sent: Saturday, March 05, 2005 8:31 AM
To: pthreads-win32@sources.redhat.com
Subject: Re: Handle leak when using pthread mutex with win32 api threads

> > >        static pthread_mutex_t 
> > > mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> >
> > that line above is not thread safe.  you might initialize the same 
> > mutex multiple times.  Not good.
> >
> 
> The handle leak comes about because, in pthreads-win32, 
> pthread_mutex_t is only a pointer, and the actual mutex struct is 
> malloced by the library, in this case, the first time you call
pthread_mutex_lock().
> That is, the library just calls pthread_mutex_init() for you. Also, 
> you must always call pthread_mutex_destroy() to clean up, just the 
> same as if you had called pthread_mutex_init() yourself.

I've made small corrections in test sample, so now mutex initialised
only once, and i've added pthread_mutex_destroy() function call. But the
problem still remains.
Here is two test applications - one of them uses API threads, and the
other pthreads. Program with pthreads don't have any leaks, while
program with API threads produces lots of them.

//program with handle leaks
#include <pthread.h>
#include <windows.h>
#include <conio.h>

pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);

DWORD WINAPI threadProc(void *param)
{
	pthread_mutex_lock(&mutex);
	pthread_mutex_unlock(&mutex);
	return 0;
}

void main()
{
	const int max_threads = 500;
	DWORD id;
	HANDLE th[max_threads];
	int i;
	for (i=0;i<max_threads;i++)
	th[i]=CreateThread(0,0,threadProc,0,0,&id);

	for (i=0;i<max_threads;i++)
	{
		WaitForSingleObject(th[i],INFINITE);
		CloseHandle(th[i]);
	}
	pthread_mutex_destroy(&mutex);
	getch();
}

==========================

//program without handle leaks
#include <pthread.h>
#include <windows.h>
#include <conio.h>


pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);

void* threadProc(void *param)
{
	pthread_mutex_lock(&mutex);
	pthread_mutex_unlock(&mutex);
    return 0;
}

void main()
{
	const int max_threads = 500;
	DWORD id;
	pthread_t th[max_threads];
	int i;

	for (i=0;i<max_threads;i++)
			pthread_create(&(th[i]),0,threadProc,(void*)i);

	for (i=0;i<max_threads;i++)
		pthread_join(th[i],(void**)&id);

	pthread_mutex_destroy(&mutex);
	getch();
}

Best regards,
Dmitrii Sernii 
  
       This message may contain privileged and/or confidential information.  If you have received this e-mail in error or are not the intended recipient, you may not use, copy, disseminate or distribute it; do not open any attachments, delete it immediately from your system and notify the sender promptly by e-mail that you have done so.  Thank you. 
 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
@ 2005-03-06  8:54 Dmitrii Sernii
  0 siblings, 0 replies; 9+ messages in thread
From: Dmitrii Sernii @ 2005-03-06  8:54 UTC (permalink / raw)
  To: pthreads-win32

>> I've made small corrections in test sample, so now mutex initialised
>> only once, and i've added pthread_mutex_destroy() function call. But
>> the problem still remains.
>> Here is two test applications - one of them uses API threads, and the
>> other pthreads. Program with pthreads don't have any leaks, while
>> program with API threads produces lots of them.
>> 

RJ> Are you dynamic or static linking? If you're using the pthreads dll then
RJ> there would seem to be a problem in the library. If you're static
RJ> linking then read on ...

I'm using pthreads dll.


Best regards,
Dmitrii Sernii

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
  2005-03-05 13:31 Dmitrii Sernii
@ 2005-03-05 21:41 ` Ross Johnson
  2005-03-08  1:22 ` Ross Johnson
  1 sibling, 0 replies; 9+ messages in thread
From: Ross Johnson @ 2005-03-05 21:41 UTC (permalink / raw)
  To: Pthreads-Win32 list

On Sat, 2005-03-05 at 15:31 +0200, Dmitrii Sernii wrote:

> I've made small corrections in test sample, so now mutex initialised
> only once, and i've added pthread_mutex_destroy() function call. But
> the problem still remains.
> Here is two test applications - one of them uses API threads, and the
> other pthreads. Program with pthreads don't have any leaks, while
> program with API threads produces lots of them.
> 

Are you dynamic or static linking? If you're using the pthreads dll then
there would seem to be a problem in the library. If you're static
linking then read on ...

The library will be automatically creating POSIX thread handles (with
DETACHED status) because the pthread_mutex_lock() call needs a pthread
ID to track recursive mutex ownership. These pthread IDs are cleaned up
by the pthreads dll.c dllMain() switch. This cleanup doesn't happen
automatically when static linking, in which case it's necessary to
explicitly run the cleanup. There are four routines provided for this
(see README.NONPORTABLE for details):-

BOOL
pthread_win32_process_attach_np (void);

BOOL
pthread_win32_process_detach_np (void);

BOOL
pthread_win32_thread_attach_np (void);

BOOL
pthread_win32_thread_detach_np (void);


Regards.
Ross

> //program with handle leaks
> #include <pthread.h>
> #include <windows.h>
> #include <conio.h>
> 
> pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> 
> DWORD WINAPI threadProc(void *param)
> {
> 	pthread_mutex_lock(&mutex);
> 	pthread_mutex_unlock(&mutex);
> 	return 0;
> }
> 
> void main()
> {
> 	const int max_threads = 500;
> 	DWORD id;
> 	HANDLE th[max_threads];
> 	int i;
> 	for (i=0;i<max_threads;i++)
> 	th[i]=CreateThread(0,0,threadProc,0,0,&id);
> 
> 	for (i=0;i<max_threads;i++)
> 	{
> 		WaitForSingleObject(th[i],INFINITE);
> 		CloseHandle(th[i]);
> 	}
> 	pthread_mutex_destroy(&mutex);
> 	getch();
> }
> 
> ==========================
> 
> //program without handle leaks
> #include <pthread.h>
> #include <windows.h>
> #include <conio.h>
> 
> 
> pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> 
> void* threadProc(void *param)
> {
> 	pthread_mutex_lock(&mutex);
> 	pthread_mutex_unlock(&mutex);
>     return 0;
> }
> 
> void main()
> {
> 	const int max_threads = 500;
> 	DWORD id;
> 	pthread_t th[max_threads];
> 	int i;
> 
> 	for (i=0;i<max_threads;i++)
> 			pthread_create(&(th[i]),0,threadProc,(void*)i);
> 
> 	for (i=0;i<max_threads;i++)
> 		pthread_join(th[i],(void**)&id);
> 
> 	pthread_mutex_destroy(&mutex);
> 	getch();
> }
> 
> Best regards,
> Dmitrii Sernii
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Handle leak when using pthread mutex with win32 api threads
@ 2005-03-05 13:31 Dmitrii Sernii
  2005-03-05 21:41 ` Ross Johnson
  2005-03-08  1:22 ` Ross Johnson
  0 siblings, 2 replies; 9+ messages in thread
From: Dmitrii Sernii @ 2005-03-05 13:31 UTC (permalink / raw)
  To: pthreads-win32

> > >        static pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);
> >
> > that line above is not thread safe.  you might initialize the same
> > mutex multiple times.  Not good.
> >
> 
> The handle leak comes about because, in pthreads-win32, pthread_mutex_t
> is only a pointer, and the actual mutex struct is malloced by the
> library, in this case, the first time you call pthread_mutex_lock().
> That is, the library just calls pthread_mutex_init() for you. Also, you
> must always call pthread_mutex_destroy() to clean up, just the same as
> if you had called pthread_mutex_init() yourself.

I've made small corrections in test sample, so now mutex initialised
only once, and i've added pthread_mutex_destroy() function call. But
the problem still remains.
Here is two test applications - one of them uses API threads, and the
other pthreads. Program with pthreads don't have any leaks, while
program with API threads produces lots of them.

//program with handle leaks
#include <pthread.h>
#include <windows.h>
#include <conio.h>

pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);

DWORD WINAPI threadProc(void *param)
{
	pthread_mutex_lock(&mutex);
	pthread_mutex_unlock(&mutex);
	return 0;
}

void main()
{
	const int max_threads = 500;
	DWORD id;
	HANDLE th[max_threads];
	int i;
	for (i=0;i<max_threads;i++)
	th[i]=CreateThread(0,0,threadProc,0,0,&id);

	for (i=0;i<max_threads;i++)
	{
		WaitForSingleObject(th[i],INFINITE);
		CloseHandle(th[i]);
	}
	pthread_mutex_destroy(&mutex);
	getch();
}

==========================

//program without handle leaks
#include <pthread.h>
#include <windows.h>
#include <conio.h>


pthread_mutex_t mutex(PTHREAD_RECURSIVE_MUTEX_INITIALIZER);

void* threadProc(void *param)
{
	pthread_mutex_lock(&mutex);
	pthread_mutex_unlock(&mutex);
    return 0;
}

void main()
{
	const int max_threads = 500;
	DWORD id;
	pthread_t th[max_threads];
	int i;

	for (i=0;i<max_threads;i++)
			pthread_create(&(th[i]),0,threadProc,(void*)i);

	for (i=0;i<max_threads;i++)
		pthread_join(th[i],(void**)&id);

	pthread_mutex_destroy(&mutex);
	getch();
}

Best regards,
Dmitrii Sernii

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2005-03-08  1:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-03-04 15:53 Handle leak when using pthread mutex with win32 api threads Dmitry Sernii
2005-03-04 17:06 ` Gottlob Frege
2005-03-04 22:27   ` Ross Johnson
2005-03-04 17:07 ` Gottlob Frege
2005-03-05 13:31 Dmitrii Sernii
2005-03-05 21:41 ` Ross Johnson
2005-03-08  1:22 ` Ross Johnson
2005-03-06  8:54 Dmitrii Sernii
2005-03-06 12:51 Bossom, John

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).