Index: vm/vmcore/include/jarfile_support.h =================================================================== --- vm/vmcore/include/jarfile_support.h (revision 634250) +++ vm/vmcore/include/jarfile_support.h (working copy) @@ -33,6 +33,7 @@ #else #include #endif +#include "apr_mmap.h" #include "properties.h" #include "manifest.h" @@ -209,31 +210,39 @@ JarEntryCache* m_entries; bool m_ownCache; Manifest* m_manifest; - tl::MemoryPool pool; + tl::MemoryPool m_pool; + // handle of the jar file + int m_file_handle; + // associated lock + Lock_Manager m_lock; + // should jar support use mmap instead of open/read + bool m_use_mmap; + // apr pool to use with mmap + apr_pool_t* m_mappool; + // apr file object for mapping + apr_file_t* m_jarfile; + // memory location for jar + apr_mmap_t* m_mmap; // list of the jar files static std::vector m_jars; public: JarFile(JarEntryCache* jec = NULL) - : m_name(NULL), m_entries(jec), m_ownCache(jec == NULL), m_manifest(NULL), jfh(0) {} - JarFile( const JarFile& jf ) : jfh(0) { - m_name = jf.m_name; - m_entries = jf.m_entries; - m_manifest = new Manifest(jf.m_manifest); - } + : m_name(NULL), m_entries(jec), m_ownCache(jec == NULL), m_manifest(NULL), m_file_handle(0), + m_use_mmap(false), m_mmap(NULL) {} ~JarFile() { if(m_ownCache) m_entries->~JarEntryCache(); m_ownCache = false; m_entries = NULL; - if (jfh != 0) close(jfh); - jfh = 0; + if (m_file_handle != 0) close(m_file_handle); + m_file_handle = 0; if( m_manifest ) delete m_manifest; m_manifest = NULL; } // parses JAR file and stores its structure inside - bool Parse( const char* filename ); + bool Parse( const char* filename, bool do_map ); // returns entry cache (either shared or owned) JarEntryCache* GetCache() { return m_entries; } // returns if this JarFile owns entry cache it holds @@ -241,6 +250,7 @@ const JarEntry* Lookup( const char* je_name ) const { return m_entries->Lookup(je_name); } + bool ReadEntry(unsigned char* buf, long entry_offset, int entry_length); // returns manifest from parsed jar archive Manifest* GetManifest() { return m_manifest; } // returns JAR file name @@ -250,10 +260,6 @@ static JarFile* GetJar(int idx) { return m_jars[idx]; } - - // handle of the jar file - int jfh; - Lock_Manager lock; }; // class JarFile Index: vm/vmcore/include/environment.h =================================================================== --- vm/vmcore/include/environment.h (revision 634250) +++ vm/vmcore/include/environment.h (working copy) @@ -97,6 +97,12 @@ bool use_common_jar_cache; /** + * If set to true, jar files are mapped into memory instead of reading + * them from disk. + */ + bool map_bootsrtap_jars; + + /** * If set to true by the -compact_fields command-line option, * the VM will not pad out fields of less than 32 bits to four bytes. * However, fields will still be aligned to a natural boundary, Index: vm/vmcore/src/class_support/classloader.cpp =================================================================== --- vm/vmcore/src/class_support/classloader.cpp (revision 634250) +++ vm/vmcore/src/class_support/classloader.cpp (working copy) @@ -1182,7 +1182,8 @@ void* mem_JarFile = apr_palloc(pool, sizeof(JarFile)); element->m_jar = new (mem_JarFile) JarFile(jec); - if(element->m_jar && element->m_jar->Parse(new_path->bytes)) { + if(element->m_jar && element->m_jar->Parse(new_path->bytes, m_env->map_bootsrtap_jars)) + { TRACE2("classloader.jar", "opened archive: " << new_path ); } } Index: vm/vmcore/src/init/vm_init.cpp =================================================================== --- vm/vmcore/src/init/vm_init.cpp (revision 634250) +++ vm/vmcore/src/init/vm_init.cpp (working copy) @@ -762,6 +762,7 @@ vm_env->sort_fields = get_boolean_property("vm.sort_fields", vm_env->sort_fields, VM_PROPERTIES); vm_env->compact_fields = get_boolean_property("vm.compact_fields", vm_env->compact_fields, VM_PROPERTIES); vm_env->use_common_jar_cache = get_boolean_property("vm.common_jar_cache", TRUE, VM_PROPERTIES); + vm_env->map_bootsrtap_jars = get_boolean_property("vm.map_bootstrap_jars", TRUE, VM_PROPERTIES); vm_env->init_pools(); Index: vm/vmcore/src/util/jarfile_support.cpp =================================================================== --- vm/vmcore/src/util/jarfile_support.cpp (revision 634250) +++ vm/vmcore/src/util/jarfile_support.cpp (working copy) @@ -54,19 +54,11 @@ { // length of content should be enough for storing m_size_uncompressed bytes JarFile* jf = JarFile::GetJar(m_jarFileIdx); - int inFile = jf->jfh; - // gregory - It is necessary to lock per jar file because the file - // handle is global to all threads, and file operations like seek, - // read, etc may be confused when many threads operate on the same - // jar file - LMAutoUnlock lock(&jf->lock); - if( lseek( inFile, m_contentOffset, SEEK_SET ) == -1 ) return false; - switch( m_method ) { case JAR_FILE_STORED: - return ( read( inFile, content, m_sizeCompressed ) == m_sizeCompressed ); + return jf->ReadEntry(content, m_contentOffset, m_sizeCompressed); case JAR_FILE_SHRUNK: printf( "Found SHRUNK content. No support as of yet.\n" ); return false; @@ -96,7 +88,7 @@ { unsigned char* data = (unsigned char*)STD_MALLOC(m_sizeCompressed + 1); // FIXME: check that memory was allocated - if( read( inFile, data, m_sizeCompressed ) < m_sizeCompressed ) { + if(!jf->ReadEntry(data, m_contentOffset, m_sizeCompressed)) { STD_FREE(data); return false; } @@ -121,6 +113,8 @@ infRes = inflate( &inf, Z_FINISH ); STD_FREE(data); if( infRes != Z_STREAM_END ) { + // FIXME: this is broken stream actually + // and breaking will result in "Invalid class magic" later on break; } infRes = inflateEnd( &inf ); @@ -145,31 +139,60 @@ #pragma warning( disable: 4786 ) // identifier was truncated to 255 characters in the browser information #endif -bool JarFile::Parse( const char* fileName ) +bool JarFile::Parse( const char* fileName, bool do_map ) { - int flags = O_RDONLY; + int fp; + long off; + unsigned char* buf; + unsigned long fsize = 0; + + m_use_mmap = do_map; + m_name = fileName; + + if(!m_use_mmap) { + int flags = O_RDONLY; #ifndef PLATFORM_POSIX - flags |= O_BINARY; + flags |= O_BINARY; #endif - int fp = open( fileName, flags, 0 ); - jfh = fp; - if( fp == -1 ) return false; + fp = open( fileName, flags, 0 ); + if( fp == -1 ) return false; - m_name = fileName; + m_file_handle = fp; - struct stat fs; - if(stat(fileName, &fs) == -1) return false; - unsigned long fsize = fs.st_size; + struct stat fs; + if(stat(fileName, &fs) == -1) return false; + fsize = fs.st_size; - m_jars.push_back(this); + int cd_size = fsize < 67000 ? fsize : 67000; + lseek(fp, -cd_size, SEEK_END); + buf = (unsigned char*)STD_ALLOCA(cd_size); + off = read(fp, buf, cd_size) - 22; // 22 - EOD size + } else { + apr_pool_create(&m_mappool, NULL); - int cd_size = fsize < 67000 ? fsize : 67000; - lseek(fp, -cd_size, SEEK_END); - unsigned char *buf = (unsigned char *)STD_ALLOCA(cd_size); - long off = read(fp, buf, cd_size) - 22; // 22 - EOD size + apr_status_t status = + apr_file_open(&m_jarfile, fileName, APR_READ, APR_OS_DEFAULT, m_mappool); + assert(!status); + + apr_off_t _fsize = 0; + status = apr_file_seek(m_jarfile, APR_END, &_fsize); + assert(!status); + fsize = (unsigned long)_fsize; + + status = apr_mmap_create(&m_mmap, m_jarfile, 0, (apr_size_t)fsize, + APR_MMAP_READ, m_mappool); + assert(!status); + + int cd_size = fsize < 67000 ? fsize : 67000; + off = cd_size - 22; + buf = (unsigned char*)m_mmap->mm + fsize - cd_size; + } + long offsetCD; // start of the Central Dir int number; // number of entries + m_jars.push_back(this); + JarEntry je; je.m_jarFileIdx = (int)(m_jars.size() - 1); @@ -186,18 +209,19 @@ return false; } - m_manifest = new Manifest(); - if(!m_manifest) return false; - if(m_ownCache) { - void* jec_mem = pool.alloc(sizeof(JarEntryCache)); + void* jec_mem = m_pool.alloc(sizeof(JarEntryCache)); m_entries = new (jec_mem) JarEntryCache(); } - lseek(fp, offsetCD, SEEK_SET); + if(!m_use_mmap) { + lseek(fp, offsetCD, SEEK_SET); - buf = (unsigned char *)STD_MALLOC(fsize - offsetCD); - fsize = read(fp, buf, fsize - offsetCD); + buf = (unsigned char *)STD_MALLOC(fsize - offsetCD); + fsize = read(fp, buf, fsize - offsetCD); + } else { + buf = (unsigned char*)m_mmap->mm + offsetCD; + } off = 0; for (int i = 0; i < number; i++){ @@ -205,14 +229,15 @@ return false; je.ConstructFixed(buf + off); - je.m_fileName = (char *)pool.alloc(je.m_nameLength + 1); + je.m_fileName = (char *)m_pool.alloc(je.m_nameLength + 1); strncpy(je.m_fileName, (const char *)buf + off + JAR_DIRECTORYENTRY_LEN, je.m_nameLength ); je.m_fileName[je.m_nameLength] = '\0'; je.m_contentOffset = je.m_relOffset + JarEntry::sizeFixed + je.m_nameLength + je.m_extraLength; if(!strcmp(je.m_fileName, "META-INF/MANIFEST.MF")) { // parse manifest - if(!m_manifest->Parse(&je)) return false; - if(!(*m_manifest)) { + m_manifest = new Manifest(); + if(!m_manifest) return false; + if(!m_manifest->Parse(&je) || !(*m_manifest)) { delete m_manifest; return false; } @@ -225,11 +250,33 @@ } off += JAR_DIRECTORYENTRY_LEN + je.m_nameLength + je.m_extraLength; } - STD_FREE(buf); + if(!m_use_mmap) { + STD_FREE(buf); + } return true; } + +bool JarFile::ReadEntry(unsigned char* buf, long entry_offset, int entry_length) +{ + if(!m_use_mmap) { + // gregory - It is necessary to lock per jar file because the file + // handle is global to all threads, and file operations like seek, + // read, etc may be confused when many threads operate on the same + // jar file + LMAutoUnlock llock(&m_lock); + + if(lseek(m_file_handle, entry_offset, SEEK_SET) == -1) return false; + + if(read(m_file_handle, buf, entry_length) < entry_length) return false; + } else { + memcpy(buf, (char*)m_mmap->mm + entry_offset, entry_length*sizeof(char)); + } + + return true; +} + #ifndef PLATFORM_POSIX #pragma warning( default: 4786 ) // identifier was truncated to 255 characters in the browser information #endif