void child() { for (int i=1; i<=max_num; i++) { cq.push(i); } // Queue is closed only if all child threads are finished if(bar.wait()) cq.close(); }
//! executing filter /*override*/ void* operator()(void*) { int n = --N; if(n <= 0) return 0; Queue.push( Matrix1110 ); return &Queue; }
int main() { srand(time(0)); // Custom scheduling is not required - can be integrated // to other systems transparently main_tasks.push([] { asynchronous([] { return async_user_handler(), finished = true; }); }); Task task; while(!finished) { main_tasks.pop(task); task(); } cout << "Done" << std::endl; return EXIT_SUCCESS; }
static void* ccr_worker(void *arg) { int r, resume; process_t *proc; while (1) { // Checks if threads need to be killed and the the ready procces's queue is empty // That way only when the queue is empty the threads are killed // if ((free_flag.compare_and_swap(false, true)) && (prc_ready.empty())) if (free_flag.compare_and_swap(false, true)) { pthread_t thread = pthread_self(); // removes reference from the pool thr_pool.remove(thread); // checks if threre's more threads to kill and set the flag if (free_workers.fetch_and_decrement() > 1) { free_flag.compare_and_swap(true, false); } //kills the current thread pthread_exit(NULL); } prc_ready.pop(proc); if (!proc) return NULL; r = lua_resume(proc->L, 0); switch (r) { case LUA_YIELD: //cerr << "Yield!\n"; switch (proc->status) { case PS_READY: // releasing the lock acquired in ccr_yield proc->wlock.release(); prc_ready.push(proc); break; case PS_BLOCKING: proc->status = PS_BLOCKED; // releasing the lock acquired in ccr_yield proc->wlock.release(); break; } break; case LUA_ERRRUN: case LUA_ERRMEM: case LUA_ERRERR: cerr << "[ERROR][PROCESSING A LUA PROCESS] " << lua_tostring(proc->L, -1) << endl; // fall-through case 0: lua_close(proc->L); mbx_close(proc->mbox); prc_free(proc); break; } } return NULL; }
void producer(std::atomic<bool>& keep_working, concurrent_queue<std::vector<char> >& sink) { while (keep_working.load()) { if (sink.size() > 10000) std::this_thread::yield(); else sink.push(std::vector<char>(rand() % 10000)); } }
//! Get pair of matricies if present bool pop_if_present( pair<Matrix2x2, Matrix2x2> &mm ) { // get first matrix if present if(!Queue.pop_if_present(mm.first)) return false; // get second matrix if present if(!Queue.pop_if_present(mm.second)) { // if not, then push back first matrix Queue.push(mm.first); return false; } return true; }
void operator() (const blocked_range<size_t>&r) const { size_t begin = r.begin(); size_t end = r.end(); for (size_t i = begin; i < end; i++) { void * tmp = tbballoc.allocate(1); memset(tmp, 1, objsize); cq.push(tmp); } }
int main(int argc, char*argv[]) { size_t nElements = 1280000; size_t nthreads = 1; struct timeval start, stop; if (argc >= 2) { nthreads = atoi(argv[1]); } if (argc >= 3) { nElements = atoi(argv[2]); } if (argc >= 4) { objsize = atoi(argv[3]); } task_scheduler_init init(nthreads); for (uintptr_t i = 0; i < nElements; i++) { cq.push((void*)i); } for (uintptr_t i = 0; i < nElements; i++) { void* j; cq.try_pop(j); assert(i == (uintptr_t)j); } assert(cq.empty()); tick_count t0 = tick_count::now(); parallel_for(blocked_range<size_t>(0, nElements), loop_queuer()); tick_count t1 = tick_count::now(); printf("queue time: %f secs\n", (t1 - t0).seconds()); t0 = tick_count::now(); parallel_for(blocked_range<size_t>(0, nElements), loop_dequeuer()); t1 = tick_count::now(); printf("dequeue time: %f secs\n", (t1 - t0).seconds()); assert(cq.empty()); t0 = tick_count::now(); parallel_for(blocked_range<size_t>(0, nElements), loop_queuer()); parallel_for(blocked_range<size_t>(0, nElements), loop_dequeuer()); t1 = tick_count::now(); printf("both time: %f secs\n", (t1 - t0).seconds()); assert(cq.empty()); printf("success!\n"); return 0; }
int ccr_rawsend(process_t *proc, message_t *msg) { queuing_rw_mutex::scoped_lock rlock; rlock.acquire(proc->lock, false); if (mbx_put(proc->mbox, msg)) { if (proc->status.compare_and_swap(PS_READY, PS_BLOCKED) == PS_BLOCKED) prc_ready.push(proc); rlock.release(); return 1; } rlock.release(); return 0; }
static int ccr_finalize(lua_State *L) { int i; process_t *proc; lua_getfield(L, LUA_REGISTRYINDEX, CCR_SELF); proc = (process_t*)lua_touserdata(L, -1); if (proc->main) { for (i = 0; i < THR_SIZE; i++) { prc_ready.push(NULL); } for (list<pthread_t>::iterator thread = thr_pool.begin(); thread!=thr_pool.end(); ++thread) { pthread_join(*thread, NULL); } } return 0; }
static int ccr_spawn(lua_State *L) { process_t *proc; const char *str = luaL_checkstring(L, 1); proc = new process_t(); if (proc) { proc->main = 0; proc->counter = 1; proc->status = PS_READY; proc->mbox = mbx_create(); if (proc->mbox) { proc->L = luaL_newstate(); if (proc->L) { // luaL_openlibs(proc->L); ccr_openlibs(proc->L); lua_pushstring(proc->L, CCR_SELF); lua_pushlightuserdata(proc->L, proc); lua_rawset(proc->L, LUA_REGISTRYINDEX); if (!luaL_loadstring(proc->L, str)) { prc_ready.push(proc); lua_pushboolean(L, 1); return 1; } cerr << "[ERROR][CREATING LUA PROCESS] " << lua_tostring(proc->L, -1) << endl; lua_close(proc->L); } mbx_free(proc->mbox); } delete proc; } lua_pushboolean(L, 0); return 1; }
int comm_request_cs( unsigned int group ) { int error = 0; // Check to see if the reply is for a group that exists if not skip group_map_t::iterator group_it = comm_groups.find( group ); if ( group_it == comm_groups.end() ) { cout << "comm_request_cs Cannot request critical section for group " << group << endl; error = -1; return error; } // Request critical section for specified group. Record stats for waiting time cs_entry_t req; req.seq_num = 0; req.wait_time = 0; req.msg_count = 0; CStopWatch timer; msg_t outgoing; outgoing.send_ID = NODE_INFO.id; outgoing.dest_ID = 0; outgoing.ts = comm_seq_num.num; outgoing.group_ID = group; outgoing.msg_type = MSG_T_REQUEST_CS; timer.startTimer(); // Lock cs entries so they don't change while reading // comm_seq_num.mutex.lock(); // group_it->second.cs.mutex.lock(); SHARED_VARS.lock(); group_it->second.cs.requesting = true; // Update and record sequence numbers comm_seq_num.num = comm_seq_num.max_num + 1; outgoing.ts = comm_seq_num.num; req.seq_num = comm_seq_num.num; for ( map<unsigned int, critical_section_t>::iterator it = group_it->second.cs.cs_map.begin(); it != group_it->second.cs.cs_map.end(); it++ ) { if ( !(it->second.mutex_token) ) { req.msg_count++; outgoing.dest_ID = it->first; comm_send( outgoing ); } } // Total messages exchanged is always 1 sent + 1 recv = 2*sent req.msg_count *= 2; // group_it->second.cs.mutex.unlock(); // comm_seq_num.mutex.unlock(); SHARED_VARS.unlock(); // If no requests were made, do not wait on any replies if ( req.msg_count > 0 ) { group_it->second.cs.entry_ok.wait( group_it->second.cs.entry_ok_mutex ); } // Record wait time timer.stopTimer(); req.wait_time = timer.getElapsedTime(); cout << "cs log entry: " << "seq_num " << req.seq_num << " wait_time = " << req.wait_time << " msg_count = " << req.msg_count << endl; comm_cs_log.push( req ); return 0; }
int main() { try { dExternalDustCurrentOffset = DUST_OFFSET_INIT_VALUE ; dExternalDustCurrentMinValue = DUST_MIN_INIT_VALUE ; bContinueExecution = true ; // Registriamo il segnale interrupt Software del S.O. scatenato con il CTRL+C e l'handler da evocare a fronte del suo // "lancio" da parte del S.O. stesso signal( SIGINT , Signal_Callback_Handler ) ; // Crea il thread che "consuma" i valori rilevati dai sensori thread threadConsumatore( ( CConsumaValoriSensori() ) ) ; // Detach perche' la sua esecuzione puo' viaggiare indipendente e noi non dobbiamo conoscerne lo stato threadConsumatore.detach() ; // Crea il thread per la lettura del sensore I2C interno // async per poterne facilmente attendere la chiusura prima del return del main, se si esce con CTRL+C future<void> threadI2C = async( launch::async , CReadI2C() ) ; // Crea il thread che "tara" i valori rilevati dal sensore delle polveri thread threadTaraPolveri( ( CTaratoreValoriPolveri() ) ) ; // Detach perche' la sua esecuzione puo' viaggiare indipendente e noi non dobbiamo conoscerne lo stato threadTaraPolveri.detach() ; CUsbUtils usbUtils ; unsigned char pReadBuffer[6] = { 0 , 0 , 0 , 0 , 0 , 0 } ; uint16_t nsReadCounter = 0 ; /* * Misuriamo le polveri ogni 10 ms e temperatura ed umidita' ogni 1000 ms */ while ( bContinueExecution ) { try { nsReadCounter++ ; int nReadedBytes = 0 ; if ( usbUtils.Read( pReadBuffer , 6 , &nReadedBytes ) ) { // Elaborazione della lettura delle polveri uint16_t nsDust = ( pReadBuffer[0] << 8 ) + pReadBuffer[1] ; externalDustMeasurements.push( CUtils::FormatDustValue( nsDust ) ) ; // Se abbiamo raggiunto il valore HUMIDITY_AND_TEMPERATURE_PERIOD_COUNTER del contatore, significa che siamo pronti per // processare anche temperatura ed umidita', oltre alla polvere if ( nsReadCounter == HUMIDITY_AND_TEMPERATURE_PERIOD_COUNTER ) { // Elaborazione della lettura della temperatura e dell'umidita' (esterne) uint16_t nsHumidity = ( pReadBuffer[2] << 8 ) + pReadBuffer[3] ; uint16_t nsTemperature = ( pReadBuffer[4] << 8 ) + pReadBuffer[5] ; if ( nsHumidity == HUMIDITY_AND_TEMPERATURE_ERR_CODE || nsTemperature == HUMIDITY_AND_TEMPERATURE_ERR_CODE ) { CUtils::AddWarningMessage( "Errore nel Sensore di Temperatura e Umidita' del PIC" ) ; } else { double dExternalTemperatureCelsius = CUtils::FormatTemperatureValue( nsTemperature ) ; double dExternalHumidityPercentage = CUtils::FormatHumidityValue( nsHumidity ) ; //cout << "Temperatura: " << dExternalTemperatureCelsius ; //cout << " Umidita': " << dExternalHumidityPercentage << endl ; // Aggiungo i valori di temperatura ed umidita' t_HumidityAndTemperatureMeasurementData humidityAndTemperatureMeasurementData ; humidityAndTemperatureMeasurementData.m_dHumidityPercentage = dExternalHumidityPercentage ; humidityAndTemperatureMeasurementData.m_dTemperatureCelsius = dExternalTemperatureCelsius ; externalHumidityAndTemperatureMeasurements.push( humidityAndTemperatureMeasurementData ) ; } nsReadCounter = 0 ; } } else { CUtils::AddWarningMessage( string( "main - usbUtils.Read fallita. VID Device: " ) + to_string( PIC_VID ) + string( " PID Device: " ) + to_string( PIC_PID ) ) ; } std::chrono::milliseconds sleep_duration( DUST_MEASUREMENT_PERIOD_MS ) ; std::this_thread::sleep_for( sleep_duration ) ; } catch( const exception& e ) { CUtils::AddWarningMessage( "main - Eccezione durante la lettura dei dati dai sensori. Testo: " + string( e.what() ) ) ; } } threadI2C.wait() ; } catch( const exception& e ) { CUtils::AddWarningMessage( "main - Eccezione generica. Testo: " + string( e.what() ) ) ; } return 0 ; }
int main(int argc, char **argv) { /* cv::Mat foo(50,1, CV_32FC3); cv::Mat bar(3,1, CV_32FC1); cv::Mat baz = bar.t() * foo.reshape(1).t(); cout << baz.size() << endl; return 0; //*/ parse_args(argc, argv); if (videofile.empty()) { std::cout << "no input file given" << std::endl; return 0; } auto vid = new cv::VideoCapture(videofile); if (!vid->isOpened()) { std::cerr << "vid could not be opened!" << std::endl; delete vid; exit(1); } // build file names char buf[1000]; std::string basename = splitext(videofile); sprintf_s(buf, "%s-foreground-r%d%s-b%dx%d-a%g-d%g-s%g-mst%g", basename.c_str(), reduction, (blendframes ? "b" : ""), blur.x, blur.y, alpha_avg, alpha_dev, sigma_dev, masksum_threshold ); std::string outbase(buf); if (suffix.length() > 0) outbase += "-" + suffix; std::cout << "outbase: " << outbase << std::endl; std::string inmaskname(basename + "-inmask.bmp"); cv::Mat inmask = cv::imread(inmaskname, cv::IMREAD_GRAYSCALE); bool const use_inmask = (inmask.data != NULL); if (use_inmask) { std::cout << "using inmask " << inmaskname << std::endl; //cv::imshow("inmask", inmask); } std::string outaudio(outbase + "-audio.raw"); // raw pcm_s16le //std::string outmask (outbase + "-mask.avi"); std::string outdiff (outbase + "-maskdiff.avi"); vid_fps = vid->get(CV_CAP_PROP_FPS); width = (int)vid->get(CV_CAP_PROP_FRAME_WIDTH); height = (int)vid->get(CV_CAP_PROP_FRAME_HEIGHT); nframes = (int)vid->get(CV_CAP_PROP_FRAME_COUNT); if (override_fps) { cout << "fps " << vid_fps << " uncorrected" << endl; cout << "nframes " << nframes << " uncorrected" << endl; // compensate nframes = nframes / vid_fps * override_fps; vid_fps = override_fps; } cout << "fps " << vid_fps << endl; cout << "nframes " << nframes << endl; assert(fmod((double)audiorate, vid_fps) < 1.0); cv::Mat frameu8(height, width, CV_8UC3, cv::Scalar::all(0.0f)); cv::Mat frame(height, width, CV_32FC3, cv::Scalar::all(0.0f)); cv::Mat gray(height, width, CV_32F, cv::Scalar::all(0.0f)); cv::Mat average(height, width, CV_32F, cv::Scalar::all(0.0f)); cv::Mat deviation(height, width, CV_32F, cv::Scalar::all(0.0f)); ValuePlot plot_avg("plot avg", 640, 360, 0, 640, 0, 1, 0.2); ValuePlot plot_diff("plot diff", 640, 360, 0, 640, -0.03, 0.03, 1/256.); ValuePlot plot_absdiff("plot absdiff", 640, 360, 0, 640, -4, 0, 1); plot_absdiff.use_vmap = true; plot_absdiff.vmap = [](float v) { return log10(v); }; ValuePlot plot_dev("plot dev", 640, 360, 0, 640, -4, 0); plot_dev.use_vmap = true; plot_dev.vmap = [](float v) { return log10(v); }; cv::Mat diff(height, width, CV_32F, cv::Scalar::all(0.0f)); cv::Mat absdiff(height, width, CV_32F, cv::Scalar::all(0.0f)); cv::Mat viewdiff(height, width, CV_32F, cv::Scalar::all(0.0f)); cv::Mat mask(height, width, CV_8U, cv::Scalar::all(0)); std::thread reader(readfn, vid); std::thread writer(writefn, outaudio, outdiff); #ifdef WIN32 SetThreadPriority(writer.native_handle(), THREAD_PRIORITY_ABOVE_NORMAL); #endif double sched = hrtimer(); double dsched = 0.25; double statsched = hrtimer(); double dstat = 1.0; double encode_fps = 0.0; double encode_alpha = 0.1; unsigned lastcount = 0, dcount = 0; // DEBUG //dstat = 0.0; for (int k = 0; k < 5; k += 1) donequeue.push(true); while (running) { MatOrNothing item; bool success = framequeue.try_pop(item); if (!success) continue; if (!item.anything) break; // FIXME: compensate for sporadic *fast* whole-image luminance fluctuations (not caught by sigma) donequeue.push(true); frame = item.frame; gray = item.gray; unsigned frameno = item.frameno; if (use_inmask) cv::subtract(gray, average, diff, inmask); else cv::subtract(gray, average, diff); cv::add(diff, 0.5, viewdiff); absdiff = cv::abs(diff); auto diffsum = (cv::sum(absdiff)[0] / (width * height)); // ===================================================================== // TODO: histogram: deviation from 'average', bin size = 0.1, -10..+10, y-log /* a_avg.push_back(cv::sum(diff)[0] / (double)(width * height)); a_dev.push_back(cv::sum(deviation)[0] / (width * height)); while (a_avg.size() > a_depth) a_avg.erase(a_avg.begin()); while (a_dev.size() > a_depth) a_dev.erase(a_dev.begin()); int histwidth = 500; double const emax = 1; double const emin = -2; a_dev.clear(); a_dev.resize(histwidth+1, 0); for (int y = 0; y < diff.rows; y += 1) for (int x = 0; x < diff.cols; x += 1) { int const bin = 100 + (int)(deviation.at<float>(y,x) / 0.001); if (bin < 0) continue; if (bin >= a_dev.size()) continue; a_dev[bin] += 1; } for (int k = 0; k < a_dev.size(); k += 1) a_dev[k] = a_dev[k] ? log10(a_dev[k]) : -1; //*/ // ===================================================================== cv::compare(1 / sigma_dev * absdiff, deviation, mask, CV_CMP_GT); auto masksum = cv::countNonZero(mask) / (double)(width * height); if (masksum > masksum_threshold) mask = 0; //cv::Mat const mix[] = { mask, mask, mask }; //cv::Mat mask3; //cv::merge(mix, 3, mask3); cv::Mat maskviewdiff(height, width, CV_32F, cv::Scalar::all(0.0f)); maskviewdiff.setTo(cv::Scalar::all(0.5f)); viewdiff.copyTo(maskviewdiff, mask); // ===================================================================== /* { cv::Mat foo; foo = 1 - deviation / 0.005; cv::cvtColor(foo, foo, CV_GRAY2BGR); cv::resize(foo, foo, preview_size, 0, 0, CV_INTER_LINEAR); for (double a = -0.1; a <= 0.1; a += 0.01) cv::line(foo, cv::Point(100 + a / 0.001, 200 - 10), cv::Point(100 + a / 0.001, 200 + 10), cv::Scalar::all(0.5), 1); for (int k = 1; k < a_dev.size(); k += 1) cv::line(foo, cv::Point(k - 1, 200 - a_dev[k - 1] / 0.1), cv::Point(k, 200 - a_dev[k] / 0.1), cv::Scalar::all(1), 1); for (int k = 1; k < a_avg.size(); k += 1) cv::line(foo, cv::Point(k - 1, 200 - a_avg[k - 1] / 0.0001), cv::Point(k, 200 - a_avg[k] / 0.0001), cv::Scalar(1, 0, 0), 1); cv::imshow("debug dev", foo); foo = 1 - absdiff / 0.005; cv::resize(foo, foo, preview_size, 0, 0, CV_INTER_LINEAR); cv::imshow("debug diff", foo); } //*/ // ===================================================================== WriteElement const tmp = { false, frameno, maskviewdiff, masksum }; writequeue.push(tmp); if (!headless && sched < hrtimer()) { sched += dsched; cv::Mat tmp; //cv::Mat display = mask3.clone(); mask.convertTo(tmp, CV_32F, 1/255.); std::ostringstream message; message << to_hms(frameno / vid_fps); message << ", #" << frameno << " @ " << vid_fps << " fps"; message << ", mask " << std::fixed << std::setprecision(6) << masksum; message << ", diff " << std::fixed << std::setprecision(6) << diffsum; cv::putText(tmp, message.str(), cv::Point(5, height - 5), CV_FONT_HERSHEY_PLAIN, width / 600, cv::Scalar::all(1.0), width / 600); cv::resize(tmp, tmp, preview_size, 0, 0, CV_INTER_AREA); cv::imshow("mask", tmp); cv::resize(average, tmp, preview_size, 0, 0, CV_INTER_AREA); cv::imshow("average", tmp); cv::log(deviation, tmp); tmp = tmp * (0.3 / log(10)) + 1.0; cv::resize(tmp, tmp, preview_size, 0, 0, CV_INTER_AREA); cv::imshow("deviation", tmp); /* //plot_avg.plot(average); plot_absdiff.plot(absdiff); plot_diff.plot(diff); plot_dev.plot(deviation); //*/ tmp = ((viewdiff - 0.5) * 5) + 0.5; cv::resize(tmp, tmp, preview_size, 0, 0, CV_INTER_AREA); cv::imshow("diff", tmp); cv::resize(maskviewdiff, tmp, preview_size, 0, 0, CV_INTER_AREA); cv::imshow("maskdiff", tmp); while (true) { int key = cv::waitKey(1); if (key == -1) break; if (key == 27) goto STOP; std::cout << "key " << key << " pressed" << std::endl; } } // updates cv::scaleAdd(absdiff - deviation, alpha_dev, deviation, deviation); cv::scaleAdd(diff, alpha_avg, average, average); if (statsched < hrtimer()) { statsched += dstat; dcount = frameno - lastcount; lastcount = frameno; encode_fps = encode_alpha * dcount + (1 - encode_alpha) * encode_fps; double timeleft = (nframes - frameno) / encode_fps; std::cout << "frame " << frameno; std::cout << ", " << std::fixed << std::setprecision(2) << encode_fps << " fps"; std::cout << ", " << std::fixed << std::setprecision(3) << (100. * frameno / nframes) << "%"; std::cout << ", ETA " << std::fixed << std::setprecision(2) << (timeleft / 60) << "min \r"; std::cout.flush(); } } STOP: running = false; WriteElement const tmp = { true }; writequeue.push(tmp); std::cout << std::endl << "done" << std::endl; if (reader.joinable()) reader.join(); if (writer.joinable()) writer.join(); vid->release(); delete vid; //odiff.release(); //omask.release(); //oaudio.close(); return 0; }
void readfn(cv::VideoCapture *vid) { int frameno = (int)vid->get(CV_CAP_PROP_POS_FRAMES); int const width = (int)vid->get(CV_CAP_PROP_FRAME_WIDTH); int const height = (int)vid->get(CV_CAP_PROP_FRAME_HEIGHT); cv::Mat blendedframe, curframe; cv::Mat floatframe; cv::Mat grayframe; blendedframe.create(cv::Size(width, height), CV_16UC3); while (running) { if (!vid->grab()) break; bool do_process = true; if (blendframes) { vid->retrieve(curframe); //curframe.convertTo(curframe, CV_16UC3); /* std::ostringstream label; label << "Frame " << frameno; cv::putText(curframe, label.str(), cv::Point(10, 20 * (frameno % 50)), cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar::all(255), 2); //*/ //std::cerr << "%reduction = " << (frameno % reduction) << std::endl; if (frameno % reduction == 0) blendedframe.setTo(0); //blendedframe = cv::max(blendedframe, curframe); cv::add(blendedframe, curframe, blendedframe, cv::noArray(), CV_16UC3); if (frameno % reduction == reduction - 1) blendedframe.convertTo(floatframe, CV_32FC3, 1.0 / (reduction * 255)); else do_process = false; /* cv::Mat foo; cv::resize(blendedframe, foo, cv::Size(640, 360)); cv::imshow("debug", foo); if (cv::waitKey(100) != -1) exit(0); //*/ //std::cerr << "do_process = " << (do_process) << std::endl; } else { if (frameno % reduction == reduction - 1) { vid->retrieve(curframe); curframe.convertTo(floatframe, CV_32FC3, 1.0 / 255); } else { do_process = false; } } frameno += 1; if (!do_process) continue; // TODO: proper bounded queue... while (running) { bool foo; bool res = donequeue.try_pop(foo); if (res) break; } // frameu8 is ready cv::cvtColor(floatframe, grayframe, CV_BGR2GRAY, 1); cv::blur(grayframe, grayframe, blur); MatOrNothing const tmp = { true, floatframe.clone(), grayframe.clone(), frameno }; framequeue.push(tmp); //std::cout << "pushing frame " << frameno << std::endl; } MatOrNothing const tmp = { false }; framequeue.push(tmp); }
~concurrent() { q.push([=]{ done=true; }); thd.join(); }