Re: reproduced elusive cygwin bug - Mailing list pgsql-cygwin

From Tom Pfau
Subject Re: reproduced elusive cygwin bug
Date
Msg-id 5C47691674725C47B02996F02C0D362105F704@exchange.rane.net
Whole thread Raw
List pgsql-cygwin
It looks like it should be possible to work around this by renaming the
file before deleting it.  That should get it out of the way allowing a
new file to be moved into it's place.

The attached patch to your unlink program renames the file by appending
a '.d' to the file name and then deletes the renamed file.  Using this
new program the third window now says 'ls: foo.d: Permission denied'.

In practice, the backend should probably append its PID or use
tempnam().

-----Original Message-----
From: Jason Tishler [mailto:jason@tishler.net]
Sent: Wednesday, January 16, 2002 9:31 AM
To: Tom Lane; Michael Adler
Cc: Pgsql-Cygwin
Subject: Re: [CYGWIN] reproduced elusive cygwin bug


Tom,
Michael,

Please post to pgsql-cygwin@postgresql.org instead of sending private
email so that others can benefit and possibly help and to record this
issue in the archives too.

On Tue, Jan 15, 2002 at 03:38:56PM -0500, Tom Lane wrote:
> Michael Adler <adler@glimpser.org> writes:
> >> Most curious.  Needless to say, that test procedure shows no
problems on
> >> Unix machines.  Perhaps a cygwin bug?
>
> > Correct. In the first thread mentioned above, you and Jason discuss
what
> > is probably happening. Windows doesn't let one process delete
another's
> > file. For some reason, it fails less gracefully here.
>
> Hmm.  Actually, the sequence of events appears to be:
>
> 1. Vacuum process deletes pg_internal.init (as it's supposed to).
>
> 2. next backend to start attempts to create a new pg_internal.init.
> It does this by creating a temporary file name, writing it, and then
> renaming it into place.  (This is to ensure we don't leave broken
files
> behind if two or more new backends try to do this at about the same
> time.  On Unix, at least, rename() is guaranteed atomic.)

I can reproduce the problem with just the above two steps...

> BTW, I do not see what connection a temp table would have with this.
> AFAICS it wouldn't matter what you vacuumed.  Does it really have to
> be a temp table?

..and I agree that temporary tables are not part of the root cause.

> It would appear that if the process that deleted pg_internal.init is
> still around, a rename to create a fresh pg_internal.init fails.  I
> cannot understand why this should be; surely it's either a Windows bug
> or a cygwin bug?

Actually, the root cause is due to the following Windows "feature":

    If one process has a file open (i.e, has a open handle), then other
    processes will not be able to perform certain operations on this
file.

Hence, until *all* handles open during the unlink() are closed, rename()
(and other operations) will fail.

So the problem is, in fact, even worse.  Consider the following
scenario:

    1.  A connects
    2.  B connects
    3.  C connects
    4.  B vacuums
    5.  D connects
    6.  B disconnects
    7.  D connects
    8.  A disconnects
    9.  C disconnects
    10. D connects

After 3, the three backends associated with each client have open
handles
to pg_internal.init (verified by the SysInternals Handle utility).
After
4, pg_internal.init is successfully deleted (verified by Cygwin's
strace).

During 5, rename() fails because the backends associated with A, B,
and C have open handles.  During 7, rename() stills fail even though
the backend that performed the vacuum has exited (and closed its handle)
because of the other open handles.  During 10, rename() succeeds because
*all* handles open during unlink() (i.e., DeleteFile()) have been
closed.

> If it's true, however, you should be able to repeat
> the problem with a couple of trivial C programs,

See attached for such programs which can be built as follows:

    $ g++ -o open open.cc
    $ g++ -o unlink unlink.cc

The following is a sample run:

    $ # window 1
    $ >foo
    open foo
    Enter any character (and CF) to continue...

    $ # window 2
    $ unlink foo
    Enter any character (and CF) to continue...

    $ # window 3
    $ # both open and unlink running
    $ handle foo | fgrep foo
    open.exe           pid: 1784   C:\home\jtishler\tmp\pgsql\foo
    unlink.exe         pid: 2284   C:\home\jtishler\tmp\pgsql\foo
    $ ls foo
    ls: foo: Permission denied
    $ # only open running
    $ ls foo
    ls: foo: Permission denied
    $ # both open and unlink have exited
    $ ls foo
    ls: foo: No such file or directory
    $ date >foo
    $ cat foo
    Wed Jan 16 09:03:18  2002

> which would be a fit setting for a bug report to the cygwin people.

IMO, this is not fixable in Cygwin.  Unfortunately, I don't think
that this is fixable in PostgreSQL either -- unless it can close all
pg_internal.init file descriptors when necessary.  Hopefully, I'm
wrong...

Jason

Attachment

pgsql-cygwin by date:

Previous
From: Tom Lane
Date:
Subject: Re: reproduced elusive cygwin bug
Next
From: Tom Lane
Date:
Subject: Re: reproduced elusive cygwin bug