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 {