Bug 607534 - Optimize custom dynamic loader to use less memory, r=cjones a=blocking-fennec
authorMichael Wu <mwu@mozilla.com>
Fri, 29 Oct 2010 12:22:28 -0700
changeset 56694 a5573c64b898ee08912751e0d4e078891615c571
parent 56693 4a093a3d6299662b1775e11e567122902f285e7f
child 56695 204b231f8d92168189d4e70d75f7250bf4544788
push idunknown
push userunknown
push dateunknown
reviewerscjones, blocking-fennec
bugs607534
milestone2.0b8pre
Bug 607534 - Optimize custom dynamic loader to use less memory, r=cjones a=blocking-fennec
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/SharedMemoryBasic_android.cpp
other-licenses/android/APKOpen.cpp
other-licenses/android/APKOpen.h
other-licenses/android/linker.c
toolkit/xre/nsAndroidStartup.cpp
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -64,16 +64,20 @@
 #include "mozilla/Omnijar.h"
 #include <sys/stat.h>
 
 #ifdef XP_WIN
 #include "nsIWinTaskbar.h"
 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
 #endif
 
+#ifdef ANDROID
+#include "APKOpen.h"
+#endif
+
 using mozilla::MonitorAutoEnter;
 using mozilla::ipc::GeckoChildProcessHost;
 
 template<>
 struct RunnableMethodTraits<GeckoChildProcessHost>
 {
     static void RetainCallee(GeckoChildProcessHost* obj) { }
     static void ReleaseCallee(GeckoChildProcessHost* obj) { }
@@ -383,16 +387,30 @@ GeckoChildProcessHost::PerformAsyncLaunc
 #endif
 
   FilePath exePath;
   GetPathToBinary(exePath);
 
 #ifdef ANDROID
   // The java wrapper unpacks this for us but can't make it executable
   chmod(exePath.value().c_str(), 0700);
+  int cacheCount = 0;
+  const struct lib_cache_info * cache = getLibraryCache();
+  nsCString cacheStr;
+  while (cache &&
+         cacheCount++ < MAX_LIB_CACHE_ENTRIES &&
+         strlen(cache->name)) {
+    mFileMap.push_back(std::pair<int,int>(cache->fd, cache->fd));
+    cacheStr.Append(cache->name);
+    cacheStr.AppendPrintf(":%d;", cache->fd);
+    cache++;
+  }
+  // fill the last arg with something if there's no cache
+  if (cacheStr.IsEmpty())
+    cacheStr.AppendLiteral("-");
 #endif
 
   // remap the IPC socket fd to a well-known int, as the OS does for
   // STDOUT_FILENO, for example
   int srcChannelFd, dstChannelFd;
   channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
   mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
 
@@ -446,16 +464,20 @@ GeckoChildProcessHost::PerformAsyncLaunc
   // can't pretend being the child that's forked off.
   std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
                                                   base::RandInt(0, std::numeric_limits<int>::max()));
   childArgv.push_back(mach_connection_name.c_str());
 #endif
 
   childArgv.push_back(childProcessType);
 
+#ifdef ANDROID
+  childArgv.push_back(cacheStr.get());
+#endif
+
   base::LaunchApp(childArgv, mFileMap,
 #if defined(OS_LINUX) || defined(OS_MACOSX)
                   newEnvVars,
 #endif
                   false, &process, arch);
 
 #ifdef XP_MACOSX
   // Wait for the child process to send us its 'task_t' data.
--- a/ipc/glue/SharedMemoryBasic_android.cpp
+++ b/ipc/glue/SharedMemoryBasic_android.cpp
@@ -51,25 +51,17 @@
 #include "base/process_util.h"
 
 #include "SharedMemoryBasic.h"
 
 //
 // Temporarily go directly to the kernel interface until we can
 // interact better with libcutils.
 //
-#define ASHMEM_DEVICE  		"/dev/ashmem"
-#define ASHMEM_NAME_LEN		256
-#define __ASHMEMIOC 0x77
-#define ASHMEM_SET_NAME		_IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
-#define ASHMEM_GET_NAME		_IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
-#define ASHMEM_SET_SIZE		_IOW(__ASHMEMIOC, 3, size_t)
-#define ASHMEM_GET_SIZE		_IO(__ASHMEMIOC, 4)
-#define ASHMEM_SET_PROT_MASK	_IOW(__ASHMEMIOC, 5, unsigned long)
-#define ASHMEM_GET_PROT_MASK	_IO(__ASHMEMIOC, 6)
+#include <linux/ashmem.h>
 
 namespace mozilla {
 namespace ipc {
 
 static void
 LogError(const char* what)
 {
   __android_log_print(ANDROID_LOG_ERROR, "Gecko",
@@ -97,17 +89,17 @@ SharedMemoryBasic::~SharedMemoryBasic()
 }
 
 bool
 SharedMemoryBasic::Create(size_t aNbytes)
 {
   NS_ABORT_IF_FALSE(-1 == mShmFd, "Already Create()d");
 
   // Carve a new instance off of /dev/ashmem
-  int shmfd = open(ASHMEM_DEVICE, O_RDWR, 0600);
+  int shmfd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
   if (-1 == shmfd) {
     LogError("ShmemAndroid::Create():open");
     return false;
   }
 
   if (ioctl(shmfd, ASHMEM_SET_SIZE, aNbytes)) {
     LogError("ShmemAndroid::Unmap():ioctl(SET_SIZE)");
     close(shmfd);
--- a/other-licenses/android/APKOpen.cpp
+++ b/other-licenses/android/APKOpen.cpp
@@ -29,29 +29,39 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+/*
+ * This custom library loading code is only meant to be called
+ * during initialization. As a result, it takes no special 
+ * precautions to be threadsafe. Any of the library loading functions
+ * like mozload should not be available to other code.
+ */
+
 #include <jni.h>
 #include <android/log.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
+#include <sys/limits.h>
 #include <errno.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <fcntl.h>
 #include <endian.h>
 #include <unistd.h>
 #include <zlib.h>
+#include <linux/ashmem.h>
 #include "dlfcn.h"
 #include "APKOpen.h"
 
 /* compression methods */
 #define STORE    0
 #define DEFLATE  8
 #define LZMA    14
 
@@ -104,17 +114,37 @@ struct cdir_end {
   uint32_t cdir_size;
   uint32_t cdir_offset;
   uint16_t comment_size;
   char     comment[0];
 } __attribute__((__packed__));
 
 static size_t zip_size;
 static int zip_fd;
-NS_EXPORT struct mapping_info * lib_mapping;
+static struct mapping_info * lib_mapping;
+
+NS_EXPORT const struct mapping_info *
+getLibraryMapping()
+{
+  return lib_mapping;
+}
+
+static int
+createAshmem(size_t bytes)
+{
+  int fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
+  if (fd < 0)
+    return -1;
+
+  if (!ioctl(fd, ASHMEM_SET_SIZE, bytes))
+    return fd;
+
+  close(fd);
+  return -1;
+}
 
 static void * map_file (const char *file)
 {
   int fd = open(file, O_RDONLY);
   if (fd == -1) {
     __android_log_print(ANDROID_LOG_ERROR, "GeckoMapFile", "map_file open %s", strerror(errno));
     return NULL;
   }
@@ -315,16 +345,90 @@ extractLib(const struct cdir_entry *entr
   ret = inflateEnd(&strm);
   if (ret != Z_OK)
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflateEnd failed: %s", strm.msg);
 
   if (strm.total_out != letoh32(entry->uncompressed_size))
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "File not fully uncompressed! %d / %d", strm.total_out, letoh32(entry->uncompressed_size));
 }
 
+static int cache_count = 0;
+static struct lib_cache_info *cache_mapping = NULL;
+
+NS_EXPORT const struct lib_cache_info *
+getLibraryCache()
+{
+  return cache_mapping;
+}
+
+static void
+ensureLibCache()
+{
+  if (!cache_mapping)
+    cache_mapping = (struct lib_cache_info *)calloc(MAX_LIB_CACHE_ENTRIES,
+                                                    sizeof(*cache_mapping));
+}
+
+static void
+fillLibCache(const char *buf)
+{
+  ensureLibCache();
+
+  char * str = strdup(buf);
+  if (!str)
+    return;
+
+  char * saveptr;
+  char * nextstr = str;
+  do {
+    struct lib_cache_info *info = &cache_mapping[cache_count];
+
+    char * name = strtok_r(nextstr, ":", &saveptr);
+    if (!name)
+      break;
+    nextstr = NULL;
+
+    char * fd_str = strtok_r(NULL, ";", &saveptr);
+    if (!fd_str)
+      break;
+
+    long int fd = strtol(fd_str, NULL, 10);
+    if (fd == LONG_MIN || fd == LONG_MAX)
+      break;
+    strncpy(info->name, name, MAX_LIB_CACHE_NAME_LEN - 1);
+    info->fd = fd;
+  } while (cache_count++ < MAX_LIB_CACHE_ENTRIES);
+  free(str);
+}
+
+static int
+lookupLibCacheFd(const char *libName)
+{
+  if (!cache_mapping)
+    return -1;
+
+  int count = cache_count;
+  while (count--) {
+    struct lib_cache_info *info = &cache_mapping[count];
+    if (!strcmp(libName, info->name))
+      return info->fd;
+  }
+  return -1;
+}
+
+static void
+addLibCacheFd(const char *libName, int fd)
+{
+  ensureLibCache();
+
+  struct lib_cache_info *info = &cache_mapping[cache_count++];
+  strncpy(info->name, libName, MAX_LIB_CACHE_NAME_LEN - 1);
+  info->fd = fd;
+}
+
 static void * mozload(const char * path, void *zip,
                       struct cdir_entry *cdir_start, uint16_t cdir_entries)
 {
 #ifdef DEBUG
   struct timeval t0, t1;
   gettimeofday(&t0, 0);
 #endif
 
@@ -333,19 +437,17 @@ static void * mozload(const char * path,
   void * data = ((void *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
   void * handle;
 
   if (extractLibs) {
     char fullpath[256];
     snprintf(fullpath, 256, "/data/data/org.mozilla.fennec/%s", path + 4);
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
     extractFile(fullpath, entry, data);
-    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: 1", fullpath);
     handle = __wrap_dlopen(fullpath, RTLD_LAZY);
-    __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: 2", fullpath);
     if (!handle)
       __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", fullpath, __wrap_dlerror());
 #ifdef DEBUG
     gettimeofday(&t1, 0);
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path,
                         (((long long)t1.tv_sec * 1000000LL) +
                           (long long)t1.tv_usec) -
                         (((long long)t0.tv_sec * 1000000LL) +
@@ -353,42 +455,58 @@ static void * mozload(const char * path,
 #endif
     return handle;
   }
 
   size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
 
   int fd = zip_fd;
   void * buf = NULL;
+  uint32_t lib_size = letoh32(entry->uncompressed_size);
   if (letoh16(file->compression) == DEFLATE) {
-    fd = -1;
-    buf = mmap(NULL, letoh32(entry->uncompressed_size),
-               PROT_READ | PROT_WRITE | PROT_EXEC,
-               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    int cache_fd = lookupLibCacheFd(path + 4);
+    fd = cache_fd;
+    if (fd < 0)
+      fd = createAshmem(lib_size);
+#ifdef DEBUG
+    else
+      __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s from cache", path + 4);
+#endif
+    if (fd < 0) {
+      __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get an ashmem buffer");
+      return NULL;
+    }
+    buf = mmap(NULL, lib_size,
+               PROT_READ | PROT_WRITE,
+               MAP_SHARED, fd, 0);
     if (buf == (void *)-1) {
       __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't mmap decompression buffer");
+      close(fd);
       return NULL;
     }
 
     offset = 0;
 
-    extractLib(entry, data, buf);
+    if (cache_fd < 0) {
+      extractLib(entry, data, buf);
+      addLibCacheFd(path + 4, fd);
+    }
     data = buf;
   }
 
 #ifdef DEBUG
-  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s with len %d and offset %d", path, letoh32(entry->uncompressed_size), offset);
+  __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s with len %d (0x%08x) and offset %d (0x%08x)", path, lib_size, lib_size, offset, offset);
 #endif
 
   handle = moz_mapped_dlopen(path, RTLD_LAZY, fd, data,
-                             letoh32(entry->uncompressed_size), offset);
+                             lib_size, offset);
   if (!handle)
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", path, __wrap_dlerror());
   if (buf)
-    munmap(buf, letoh32(entry->uncompressed_size));
+    munmap(buf, lib_size);
 
 #ifdef DEBUG
   gettimeofday(&t1, 0);
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "%s: spent %d", path, (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) -
                (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec));
 #endif
 
   return handle;
@@ -545,18 +663,22 @@ ChildProcessInit(int argc, char* argv[])
   for (i = 0; i < (argc - 1); i++) {
     if (strcmp(argv[i], "-omnijar"))
       continue;
 
     i = i + 1;
     break;
   }
 
+  fillLibCache(argv[argc - 1]);
   loadLibs(argv[i]);
 
+  // don't pass the last arg - it's only recognized by the lib cache
+  argc--;
+
   typedef GeckoProcessType (*XRE_StringToChildProcessType_t)(char*);
   typedef nsresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType);
   XRE_StringToChildProcessType_t fXRE_StringToChildProcessType =
     (XRE_StringToChildProcessType_t)__wrap_dlsym(xul_handle, "XRE_StringToChildProcessType");
   XRE_InitChildProcess_t fXRE_InitChildProcess =
     (XRE_InitChildProcess_t)__wrap_dlsym(xul_handle, "XRE_InitChildProcess");
 
   GeckoProcessType proctype = fXRE_StringToChildProcessType(argv[--argc]);
--- a/other-licenses/android/APKOpen.h
+++ b/other-licenses/android/APKOpen.h
@@ -40,11 +40,21 @@
 struct mapping_info {
   char * name;
   char * file_id;
   uintptr_t base;
   size_t len;
   size_t offset;
 };
 
-extern struct mapping_info * lib_mapping;
+const struct mapping_info * getLibraryMapping();
+
+#define MAX_LIB_CACHE_ENTRIES 32
+#define MAX_LIB_CACHE_NAME_LEN 32
+
+struct lib_cache_info {
+  char name[MAX_LIB_CACHE_NAME_LEN];
+  int fd;
+};
+
+const struct lib_cache_info * getLibraryCache();
 
 #endif /* APKOpen_h */
--- a/other-licenses/android/linker.c
+++ b/other-licenses/android/linker.c
@@ -925,28 +925,29 @@ load_segments(int fd, size_t offset, voi
             /* we want to map in the segment on a page boundary */
             tmp = base + (phdr->p_vaddr & (~PAGE_MASK));
             /* add the # of bytes we masked off above to the total length. */
             len = phdr->p_filesz + (phdr->p_vaddr & PAGE_MASK);
 
             TRACE("[ %d - Trying to load segment from '%s' @ 0x%08x "
                   "(0x%08x). p_vaddr=0x%08x p_offset=0x%08x ]\n", pid, si->name,
                   (unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset);
-            if (fd == -1) {
+            if (fd == -1 || PFLAGS_TO_PROT(phdr->p_flags) & PROT_WRITE) {
                 pbase = mmap(tmp, len, PROT_WRITE,
-                             MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
-                             0);
+                             MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
                 if (pbase != MAP_FAILED) {
                     memcpy(pbase, header + ((phdr->p_offset) & (~PAGE_MASK)), len);
                     mprotect(pbase, len, PFLAGS_TO_PROT(phdr->p_flags));
-                }
-            } else
+                } else
+                    DL_ERR("%s: Memcpy mapping of segment failed!", si->name);
+            } else {
                 pbase = mmap(tmp, len, PFLAGS_TO_PROT(phdr->p_flags),
-                             MAP_PRIVATE | MAP_FIXED, fd,
+                             MAP_SHARED | MAP_FIXED, fd,
                              offset + ((phdr->p_offset) & (~PAGE_MASK)));
+            }
             if (pbase == MAP_FAILED) {
                 DL_ERR("%d failed to map segment from '%s' @ 0x%08x (0x%08x). "
                       "p_vaddr=0x%08x p_offset=0x%08x", pid, si->name,
                       (unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset);
                 goto fail;
             }
 
             report_mapping(si->name, pbase, len, phdr->p_offset & (~PAGE_MASK));
@@ -1233,17 +1234,17 @@ load_mapped_library(const char * name, i
     si->dynamic = (unsigned *)-1;
     if (alloc_mem_region(si) < 0)
         goto fail;
 
     TRACE("[ %5d allocated memory for %s @ %p (0x%08x) ]\n",
           pid, name, (void *)si->base, (unsigned) ext_sz);
 
     /* Now actually load the library's segments into right places in memory */
-    if (load_segments(offset ? fd : -1, offset, mem, si) < 0) {
+    if (load_segments(fd, offset, mem, si) < 0) {
         if (si->ba_index >= 0) {
             ba_free(&ba_nonprelink, si->ba_index);
             si->ba_index = -1;
         }
         goto fail;
     }
 
     /* this might not be right. Technically, we don't even need this info
@@ -1389,16 +1390,38 @@ static int reloc_library(soinfo *si, Elf
 {
     Elf32_Sym *symtab = si->symtab;
     const char *strtab = si->strtab;
     Elf32_Sym *s;
     unsigned base;
     Elf32_Rel *start = rel;
     unsigned idx;
 
+    /* crappy hack to ensure we don't write into the read-only region */
+
+    /* crappy hack part 1: find the read only region */
+    int cnt;
+    void * ro_region_end = si->base;
+    Elf32_Ehdr *ehdr = (Elf32_Ehdr *)si->base;
+    Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned char *)si->base + ehdr->e_phoff);
+    for (cnt = 0; cnt < ehdr->e_phnum; ++cnt, ++phdr) {
+        if (phdr->p_type != PT_LOAD ||
+            PFLAGS_TO_PROT(phdr->p_flags) & PROT_WRITE ||
+            phdr->p_vaddr != 0)
+            continue;
+
+        ro_region_end = si->base + phdr->p_filesz;
+        break;
+    }
+
+    void * remapped_page = NULL;
+    void * copy_page = mmap(NULL, PAGE_SIZE,
+                            PROT_READ | PROT_WRITE,
+                            MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
     for (idx = 0; idx < count; ++idx) {
         unsigned type = ELF32_R_TYPE(rel->r_info);
         unsigned sym = ELF32_R_SYM(rel->r_info);
         unsigned reloc = (unsigned)(rel->r_offset + si->base);
         unsigned sym_addr = 0;
         char *sym_name = NULL;
 
         DEBUG("%5d Processing '%s' relocation at index %d\n", pid,
@@ -1474,16 +1497,33 @@ static int reloc_library(soinfo *si, Elf
 #endif
                 sym_addr = (unsigned)(s->st_value + base);
 	    }
             COUNT_RELOC(RELOC_SYMBOL);
         } else {
             s = NULL;
         }
 
+        /* crappy hack part 2: make this page writable */
+        void * reloc_page = reloc & ~PAGE_MASK;
+        if (reloc < ro_region_end && reloc_page != remapped_page) {
+            if (remapped_page != NULL)
+                mprotect(remapped_page, PAGE_SIZE, PROT_READ | PROT_EXEC);
+            memcpy(copy_page, reloc_page, PAGE_SIZE);
+            munmap(reloc_page, PAGE_SIZE);
+            if (mmap(reloc_page, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+                     MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+                     -1, 0) == MAP_FAILED)
+                DL_ERR("failed to map page for %s at 0x%08x! errno=%d",
+                       si->name, reloc_page, errno);
+
+            memcpy(reloc_page, copy_page, PAGE_SIZE);
+            remapped_page = reloc_page;
+        }
+
 /* TODO: This is ugly. Split up the relocations by arch into
  * different files.
  */
         switch(type){
 #if defined(ANDROID_ARM_LINKER)
         case R_ARM_JUMP_SLOT:
             COUNT_RELOC(RELOC_ABSOLUTE);
             MARK(rel->r_offset);
@@ -1579,16 +1619,17 @@ static int reloc_library(soinfo *si, Elf
 
         default:
             DL_ERR("%5d unknown reloc type %d @ %p (%d)",
                   pid, type, rel, (int) (rel - start));
             return -1;
         }
         rel++;
     }
+    munmap(copy_page, PAGE_SIZE);
     return 0;
 }
 
 #if defined(ANDROID_SH_LINKER)
 static int reloc_library_a(soinfo *si, Elf32_Rela *rela, unsigned count)
 {
     Elf32_Sym *symtab = si->symtab;
     const char *strtab = si->strtab;
--- a/toolkit/xre/nsAndroidStartup.cpp
+++ b/toolkit/xre/nsAndroidStartup.cpp
@@ -70,17 +70,17 @@ struct AutoAttachJavaThread {
 
     PRBool attached;
 };
 
 static void*
 GeckoStart(void *data)
 {
 #ifdef MOZ_CRASHREPORTER
-    struct mapping_info *info = lib_mapping;
+    const struct mapping_info *info = getLibraryMapping();
     while (info->name) {
       CrashReporter::AddLibraryMapping(info->name, info->file_id, info->base,
                                        info->len, info->offset);
       info++;
     }
 #endif
 
     AutoAttachJavaThread attacher;