Mercurial > hg
comparison mercurial/osutil.c @ 8330:7de68012f86e
Windows: improve performance via buffered I/O
The posixfile_nt code hits the win32 file API directly, which
essentially amounts to performing a system call for every read and
write. This is slow.
We add a C extension that lets us use a Python file object instead,
but preserve our desired POSIX-like semantics (the ability to rename
or delete a file that is being accessed).
If the C extension is not available (e.g. in a VPS environment
without a compiler), we fall back to the posixfile_nt code.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Fri, 08 May 2009 15:52:26 -0700 |
parents | aecea6934fdd |
children | dc1b9e22f288 |
comparison
equal
deleted
inserted
replaced
8329:79a12651d46b | 8330:7de68012f86e |
---|---|
7 the GNU General Public License, incorporated herein by reference. | 7 the GNU General Public License, incorporated herein by reference. |
8 */ | 8 */ |
9 | 9 |
10 #define _ATFILE_SOURCE | 10 #define _ATFILE_SOURCE |
11 #include <Python.h> | 11 #include <Python.h> |
12 #include <fcntl.h> | |
13 #include <stdio.h> | |
14 #include <string.h> | |
15 | |
12 #ifdef _WIN32 | 16 #ifdef _WIN32 |
13 #include <windows.h> | 17 # include <windows.h> |
18 # include <io.h> | |
14 #else | 19 #else |
15 #include <dirent.h> | 20 # include <dirent.h> |
16 #include <fcntl.h> | 21 # include <sys/stat.h> |
17 #include <string.h> | 22 # include <sys/types.h> |
18 #include <sys/stat.h> | 23 # include <unistd.h> |
19 #include <sys/types.h> | |
20 #include <unistd.h> | |
21 #endif | 24 #endif |
22 | 25 |
23 #ifdef _WIN32 | 26 #ifdef _WIN32 |
24 /* | 27 /* |
25 stat struct compatible with hg expectations | 28 stat struct compatible with hg expectations |
390 } | 393 } |
391 | 394 |
392 return _listdir(path, plen, wantstat, skip); | 395 return _listdir(path, plen, wantstat, skip); |
393 } | 396 } |
394 | 397 |
398 #ifdef _WIN32 | |
399 static PyObject *posixfile(PyObject *self, PyObject *args, PyObject *kwds) | |
400 { | |
401 static char *kwlist[] = {"name", "mode", "buffering", NULL}; | |
402 PyObject *file_obj = NULL; | |
403 char *name = NULL; | |
404 char *mode = "rb"; | |
405 DWORD access; | |
406 DWORD creation; | |
407 HANDLE handle; | |
408 int fd, flags = 0; | |
409 int bufsize = -1; | |
410 char m0, m1, m2; | |
411 char fpmode[4]; | |
412 int fppos = 0; | |
413 int plus; | |
414 FILE *fp; | |
415 | |
416 if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:posixfile", kwlist, | |
417 Py_FileSystemDefaultEncoding, | |
418 &name, &mode, &bufsize)) | |
419 return NULL; | |
420 | |
421 m0 = mode[0]; | |
422 m1 = m0 ? mode[1] : '\0'; | |
423 m2 = m1 ? mode[2] : '\0'; | |
424 plus = m1 == '+' || m2 == '+'; | |
425 | |
426 fpmode[fppos++] = m0; | |
427 if (m1 == 'b' || m2 == 'b') { | |
428 flags = _O_BINARY; | |
429 fpmode[fppos++] = 'b'; | |
430 } | |
431 else | |
432 flags = _O_TEXT; | |
433 if (plus) { | |
434 flags |= _O_RDWR; | |
435 access = GENERIC_READ | GENERIC_WRITE; | |
436 fpmode[fppos++] = '+'; | |
437 } | |
438 fpmode[fppos++] = '\0'; | |
439 | |
440 switch (m0) { | |
441 case 'r': | |
442 creation = OPEN_EXISTING; | |
443 if (!plus) { | |
444 flags |= _O_RDONLY; | |
445 access = GENERIC_READ; | |
446 } | |
447 break; | |
448 case 'w': | |
449 creation = CREATE_ALWAYS; | |
450 if (!plus) { | |
451 access = GENERIC_WRITE; | |
452 flags |= _O_WRONLY; | |
453 } | |
454 break; | |
455 case 'a': | |
456 creation = OPEN_ALWAYS; | |
457 flags |= _O_APPEND; | |
458 if (!plus) { | |
459 flags |= _O_WRONLY; | |
460 access = GENERIC_WRITE; | |
461 } | |
462 break; | |
463 default: | |
464 PyErr_Format(PyExc_ValueError, | |
465 "mode string must begin with one of 'r', 'w', " | |
466 "or 'a', not '%c'", m0); | |
467 goto bail; | |
468 } | |
469 | |
470 handle = CreateFile(name, access, | |
471 FILE_SHARE_READ | FILE_SHARE_WRITE | | |
472 FILE_SHARE_DELETE, | |
473 NULL, | |
474 creation, | |
475 FILE_ATTRIBUTE_NORMAL, | |
476 0); | |
477 | |
478 if (handle == INVALID_HANDLE_VALUE) { | |
479 PyErr_SetFromWindowsErrWithFilename(GetLastError(), name); | |
480 goto bail; | |
481 } | |
482 | |
483 fd = _open_osfhandle((intptr_t) handle, flags); | |
484 if (fd == -1) { | |
485 CloseHandle(handle); | |
486 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name); | |
487 goto bail; | |
488 } | |
489 | |
490 fp = _fdopen(fd, fpmode); | |
491 if (fp == NULL) { | |
492 _close(fd); | |
493 PyErr_SetFromErrnoWithFilename(PyExc_IOError, name); | |
494 goto bail; | |
495 } | |
496 | |
497 file_obj = PyFile_FromFile(fp, name, mode, fclose); | |
498 if (file_obj == NULL) { | |
499 fclose(fp); | |
500 goto bail; | |
501 } | |
502 | |
503 PyFile_SetBufSize(file_obj, bufsize); | |
504 bail: | |
505 PyMem_Free(name); | |
506 return file_obj; | |
507 } | |
508 #endif | |
509 | |
395 static char osutil_doc[] = "Native operating system services."; | 510 static char osutil_doc[] = "Native operating system services."; |
396 | 511 |
397 static PyMethodDef methods[] = { | 512 static PyMethodDef methods[] = { |
398 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS, | 513 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS, |
399 "list a directory\n"}, | 514 "list a directory\n"}, |
515 #ifdef _WIN32 | |
516 {"posixfile", (PyCFunction)posixfile, METH_VARARGS | METH_KEYWORDS, | |
517 "Open a file with POSIX-like semantics.\n" | |
518 "On error, this function may raise either a WindowsError or an IOError."}, | |
519 #endif | |
400 {NULL, NULL} | 520 {NULL, NULL} |
401 }; | 521 }; |
402 | 522 |
403 PyMODINIT_FUNC initosutil(void) | 523 PyMODINIT_FUNC initosutil(void) |
404 { | 524 { |