void timeDelay(LLCoros::self& self, LLPanelMarketplaceOutbox* outboxPanel)
{
	waitForEventOn(self, "mainloop");

	LLTimer delayTimer;
	delayTimer.reset();
	delayTimer.setTimerExpirySec(5.0f);

	while (!delayTimer.hasExpired())
	{
		waitForEventOn(self, "mainloop");
	}

	outboxPanel->onSyncComplete();

	gTimeDelayDebugFunc = "";
}
Exemple #2
0
void LLVertexBuffer::clientCopy(F64 max_time)
{
	if (!sDeleteList.empty())
	{
		size_t num = sDeleteList.size();
		glDeleteBuffersARB(sDeleteList.size(), (GLuint*) &(sDeleteList[0]));
		sDeleteList.clear();
		sGLCount -= num;
	}

	if (sEnableVBOs)
	{
		LLTimer timer;
		BOOL reset = TRUE;
		buffer_list_t::iterator iter = sLockedList.begin();
		while(iter != sLockedList.end())
		{
			LLVertexBuffer* buffer = *iter;
			if (buffer->isLocked() && buffer->useVBOs())
			{
				buffer->setBuffer(0);
			}
			++iter;
			if (reset)
			{
				reset = FALSE;
				timer.reset(); //skip first copy (don't count pipeline stall)
			}
			else
			{
				if (timer.getElapsedTimeF64() > max_time)
				{
					break;
				}
			}

		}

		sLockedList.erase(sLockedList.begin(), iter);
	}
}
		void init()
		{
			pLogFrame = NULL;
			pLogStartTask = NULL;
			pLogEndTask = NULL;
			pLogTickTask = NULL;
			
			// This is okay for now. We'll only need that for measuring under well defined circumstances.
			// Maybe create an installer for this one day (very low prio) that copies and registers the provider.

			hETW = ::LoadLibrary( L"c:\\xperf\\fs_etw.dll" );
			if( hETW )
			{
				tpInitialize pInitialize = reinterpret_cast< tpInitialize>( ::GetProcAddress( hETW, "initialize" ) );
				if( pInitialize && (*pInitialize)() )
				{
					pLogFrame = reinterpret_cast< tpLogFrame >( ::GetProcAddress( hETW, "log_beginFrame" ) );
					pLogStartTask = reinterpret_cast< tpLogStartTask >( ::GetProcAddress( hETW, "log_startTask" ) );
					pLogEndTask = reinterpret_cast< tpLogEndTask >( ::GetProcAddress( hETW, "log_endTask" ) );
					pLogTickTask = reinterpret_cast< tpLogTickTask >( ::GetProcAddress( hETW, "log_tickTask" ) );
					etwFrameTimer.reset();
				}
			}
		}
Exemple #4
0
BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host)
{
	//<edit>
	LLMessageLog::log(LLHost(16777343, gMessageSystem->getListenPort()), host, (U8*)send_buffer, buf_size);
	//</edit>
	BOOL status = TRUE;
	if (!mUseOutThrottle)
	{
		return doSendPacket(h_socket, send_buffer, buf_size, host );
	}
	else
	{
		mActualBitsOut += buf_size * 8;
		LLPacketBuffer *packetp = NULL;
		// See if we've got enough throttle to send a packet.
		while (!mOutThrottle.checkOverflow(0.f))
		{
			// While we have enough bandwidth, send a packet from the queue or the current packet

			S32 packet_size = 0;
			if (!mSendQueue.empty())
			{
				// Send a packet off of the queue
				LLPacketBuffer *packetp = mSendQueue.front();
				mSendQueue.pop();

				mOutBufferLength -= packetp->getSize();
				packet_size = packetp->getSize();

				status = doSendPacket(h_socket, packetp->getData(), packet_size, packetp->getHost());
				
				delete packetp;
				// Update the throttle
				mOutThrottle.throttleOverflow(packet_size * 8.f);
			}
			else
			{
				// If the queue's empty, we can just send this packet right away.
				status =  doSendPacket(h_socket, send_buffer, buf_size, host );
				packet_size = buf_size;

				// Update the throttle
				mOutThrottle.throttleOverflow(packet_size * 8.f);

				// This was the packet we're sending now, there are no other packets
				// that we need to send
				return status;
			}

		}

		// We haven't sent the incoming packet, add it to the queue
		if (mOutBufferLength + buf_size > mMaxBufferLength)
		{
			// Nuke this packet, we overflowed the buffer.
			// Toss it.
			llwarns << "Throwing away outbound packet, overflowing buffer" << llendl;
		}
		else
		{
			static LLTimer queue_timer;
			if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f)
			{
				// Add it to the queue
				llinfos << "Outbound packet queue " << mOutBufferLength << " bytes" << llendl;
				queue_timer.reset();
			}
			packetp = new LLPacketBuffer(host, send_buffer, buf_size);

			mOutBufferLength += packetp->getSize();
			mSendQueue.push(packetp);
		}
	}

	return status;
}
// decode a given message
BOOL LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender )
{
	llassert( mReceiveSize >= 0 );
	llassert( mCurrentRMessageTemplate);
	llassert( !mCurrentRMessageData );
	delete mCurrentRMessageData; // just to make sure

	// The offset tells us how may bytes to skip after the end of the
	// message name.
	U8 offset = buffer[PHL_OFFSET];
	S32 decode_pos = LL_PACKET_ID_SIZE + (S32)(mCurrentRMessageTemplate->mFrequency) + offset;

	// create base working data set
	mCurrentRMessageData = new LLMsgData(mCurrentRMessageTemplate->mName);
	
	// loop through the template building the data structure as we go
	LLMessageTemplate::message_block_map_t::const_iterator iter;
	for(iter = mCurrentRMessageTemplate->mMemberBlocks.begin();
		iter != mCurrentRMessageTemplate->mMemberBlocks.end();
		++iter)
	{
		LLMessageBlock* mbci = *iter;
		U8	repeat_number;
		S32	i;

		// how many of this block?

		if (mbci->mType == MBT_SINGLE)
		{
			// just one
			repeat_number = 1;
		}
		else if (mbci->mType == MBT_MULTIPLE)
		{
			// a known number
			repeat_number = mbci->mNumber;
		}
		else if (mbci->mType == MBT_VARIABLE)
		{
			// need to read the number from the message
			// repeat number is a single byte
			if (decode_pos >= mReceiveSize)
			{
				// commented out - hetgrid says that missing variable blocks
				// at end of message are legal
				// logRanOffEndOfPacket(sender, decode_pos, 1);

				// default to 0 repeats
				repeat_number = 0;
			}
			else
			{
				repeat_number = buffer[decode_pos];
				decode_pos++;
			}
		}
		else
		{
			llerrs << "Unknown block type" << llendl;
			return FALSE;
		}

		LLMsgBlkData* cur_data_block = NULL;

		// now loop through the block
		for (i = 0; i < repeat_number; i++)
		{
			if (i)
			{
				// build new name to prevent collisions
				// TODO: This should really change to a vector
				cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number);
				cur_data_block->mName = mbci->mName + i;
			}
			else
			{
				cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number);
			}

			// add the block to the message
			mCurrentRMessageData->addBlock(cur_data_block);

			// now read the variables
			for (LLMessageBlock::message_variable_map_t::const_iterator iter = 
					 mbci->mMemberVariables.begin();
				 iter != mbci->mMemberVariables.end(); iter++)
			{
				const LLMessageVariable& mvci = **iter;

				// ok, build out the variables
				// add variable block
				cur_data_block->addVariable(mvci.getName(), mvci.getType());

				// what type of variable?
				if (mvci.getType() == MVT_VARIABLE)
				{
					// variable, get the number of bytes to read from the template
					S32 data_size = mvci.getSize();
					U8 tsizeb = 0;
					U16 tsizeh = 0;
					U32 tsize = 0;

					if ((decode_pos + data_size) > mReceiveSize)
					{
						logRanOffEndOfPacket(sender, decode_pos, data_size);

						// default to 0 length variable blocks
						tsize = 0;
					}
					else
					{
						switch(data_size)
						{
						case 1:
							htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1);
							tsize = tsizeb;
							break;
						case 2:
							htonmemcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2);
							tsize = tsizeh;
							break;
						case 4:
							htonmemcpy(&tsize, &buffer[decode_pos], MVT_U32, 4);
							break;
						default:
							llerrs << "Attempting to read variable field with unknown size of " << data_size << llendl;
							break;
						}
					}
					decode_pos += data_size;

					cur_data_block->addData(mvci.getName(), &buffer[decode_pos], tsize, mvci.getType());
					decode_pos += tsize;
				}
				else
				{
					// fixed!
					// so, copy data pointer and set data size to fixed size
					if ((decode_pos + mvci.getSize()) > mReceiveSize)
					{
						logRanOffEndOfPacket(sender, decode_pos, mvci.getSize());

						// default to 0s.
						U32 size = mvci.getSize();
						std::vector<U8> data(size);
						if (size) memset(&(data[0]), 0, size);
						cur_data_block->addData(mvci.getName(), &(data[0]), 
												size, mvci.getType());
					}
					else
					{
						cur_data_block->addData(mvci.getName(), 
												&buffer[decode_pos], 
												mvci.getSize(), 
												mvci.getType());
					}
					decode_pos += mvci.getSize();
				}
			}
		}
	}

	if (mCurrentRMessageData->mMemberBlocks.empty()
		&& !mCurrentRMessageTemplate->mMemberBlocks.empty())
	{
		lldebugs << "Empty message '" << mCurrentRMessageTemplate->mName << "' (no blocks)" << llendl;
		return FALSE;
	}

	{
		static LLTimer decode_timer;

		if(LLMessageReader::getTimeDecodes() || gMessageSystem->getTimingCallback())
		{
			decode_timer.reset();
		}

		{
			LLFastTimer t(LLFastTimer::FTM_PROCESS_MESSAGES);
			if( !mCurrentRMessageTemplate->callHandlerFunc(gMessageSystem) )
			{
				llwarns << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << llendl;
			}
		}

		if(LLMessageReader::getTimeDecodes() || gMessageSystem->getTimingCallback())
		{
			F32 decode_time = decode_timer.getElapsedTimeF32();

			if (gMessageSystem->getTimingCallback())
			{
				(gMessageSystem->getTimingCallback())(mCurrentRMessageTemplate->mName,
								decode_time,
								gMessageSystem->getTimingCallbackData());
			}

			if (LLMessageReader::getTimeDecodes())
			{
				mCurrentRMessageTemplate->mDecodeTimeThisFrame += decode_time;

				mCurrentRMessageTemplate->mTotalDecoded++;
				mCurrentRMessageTemplate->mTotalDecodeTime += decode_time;

				if( mCurrentRMessageTemplate->mMaxDecodeTimePerMsg < decode_time )
				{
					mCurrentRMessageTemplate->mMaxDecodeTimePerMsg = decode_time;
				}


				if(decode_time > LLMessageReader::getTimeDecodesSpamThreshold())
				{
					lldebugs << "--------- Message " << mCurrentRMessageTemplate->mName << " decode took " << decode_time << " seconds. (" <<
						mCurrentRMessageTemplate->mMaxDecodeTimePerMsg << " max, " <<
						(mCurrentRMessageTemplate->mTotalDecodeTime / mCurrentRMessageTemplate->mTotalDecoded) << " avg)" << llendl;
				}
			}
		}
	}
	return TRUE;
}
Exemple #6
0
void LLPanelGroupGeneral::updateMembers()
{
	mPendingMemberUpdate = FALSE;

	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);

	if (!mListVisibleMembers || !gdatap 
		|| !gdatap->isMemberDataComplete())
	{
		return;
	}

	static LLTimer all_timer;
	static LLTimer sd_timer;
	static LLTimer element_timer;

	all_timer.reset();
	S32 i = 0;

	for( ; mMemberProgress != gdatap->mMembers.end() && i<UPDATE_MEMBERS_PER_FRAME; 
			++mMemberProgress, ++i)
	{
		//llinfos << "Adding " << iter->first << ", " << iter->second->getTitle() << llendl;
		LLGroupMemberData* member = mMemberProgress->second;
		if (!member)
		{
			continue;
		}
		// Owners show up in bold.
		std::string style = "NORMAL";
		if ( member->isOwner() )
		{
			style = "BOLD";
		}
		
		sd_timer.reset();
		LLSD row;
		row["id"] = member->getID();

		row["columns"][0]["column"] = "name";
		row["columns"][0]["font-style"] = style;
		// value is filled in by name list control

		row["columns"][1]["column"] = "title";
		row["columns"][1]["value"] = member->getTitle();
		row["columns"][1]["font-style"] = style;
		
		row["columns"][2]["column"] = "online";
		row["columns"][2]["value"] = member->getOnlineStatus();
		row["columns"][2]["font-style"] = style;

		sSDTime += sd_timer.getElapsedTimeF32();

		element_timer.reset();
		mListVisibleMembers->addElement(row);//, ADD_SORTED);
		sElementTime += element_timer.getElapsedTimeF32();
	}
	sAllTime += all_timer.getElapsedTimeF32();

	llinfos << "Updated " << i << " of " << UPDATE_MEMBERS_PER_FRAME << "members in the list." << llendl;
	if (mMemberProgress == gdatap->mMembers.end())
	{
		llinfos << "   member list completed." << llendl;
		mListVisibleMembers->setEnabled(TRUE);

		llinfos << "All Time: " << sAllTime << llendl;
		llinfos << "SD Time: " << sSDTime << llendl;
		llinfos << "Element Time: " << sElementTime << llendl;
	}
	else
	{
		mPendingMemberUpdate = TRUE;
		mListVisibleMembers->setEnabled(FALSE);
	}
}
int main(int argc, char **argv)
#endif
{
	ll_init_apr();

	// Set up llerror logging
	{
		LLError::initForApplication(".");
		LLError::setDefaultLevel(LLError::LEVEL_INFO);
//		LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG);
//		LLError::logToFile("slplugin.log");
	}

#if LL_WINDOWS
	if( strlen( lpCmdLine ) == 0 )
	{
		LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL;
	};

	U32 port = 0;
	if(!LLStringUtil::convertToU32(lpCmdLine, port))
	{
		LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
	};

	// Insert our exception handler into the system so this plugin doesn't
	// display a crash message if something bad happens. The host app will
	// see the missing heartbeat and log appropriately.
	initExceptionHandler();
#elif LL_DARWIN || LL_LINUX
	if(argc < 2)
	{
		LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL;
	}

	U32 port = 0;
	if(!LLStringUtil::convertToU32(argv[1], port))
	{
		LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
	}

	// Catch signals that most kinds of crashes will generate, and exit cleanly so the system crash dialog isn't shown.
	signal(SIGILL, &crash_handler);		// illegal instruction
# if LL_DARWIN
	signal(SIGEMT, &crash_handler);		// emulate instruction executed
# endif // LL_DARWIN
	signal(SIGFPE, &crash_handler);		// floating-point exception
	signal(SIGBUS, &crash_handler);		// bus error
	signal(SIGSEGV, &crash_handler);	// segmentation violation
	signal(SIGSYS, &crash_handler);		// non-existent system call invoked
#endif

#if LL_DARWIN
	setupCocoa();
	createAutoReleasePool();
#endif

	LLPluginProcessChild *plugin = new LLPluginProcessChild();

	plugin->init(port);

#if LL_DARWIN
		deleteAutoReleasePool();
#endif

	LLTimer timer;
	timer.start();

#if LL_WINDOWS
	checkExceptionHandler();
#endif

#if LL_DARWIN
	// If the plugin opens a new window (such as the Flash plugin's fullscreen player), we may need to bring this plugin process to the foreground.
	// Use this to track the current frontmost window and bring this process to the front if it changes.
	WindowRef front_window = NULL;
	WindowGroupRef layer_group = NULL;
	int window_hack_state = 0;
	CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group);
	if(layer_group)
	{
		// Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube)
		SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
		SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);		
	}
#endif

#if LL_DARWIN
	EventTargetRef event_target = GetEventDispatcherTarget();
#endif
	while(!plugin->isDone())
	{
#if LL_DARWIN
		createAutoReleasePool();
#endif
		timer.reset();
		plugin->idle();
#if LL_DARWIN
		{
			// Some plugins (webkit at least) will want an event loop.  This qualifies.
			EventRef event;
			if(ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &event) == noErr)
			{
				SendEventToEventTarget (event, event_target);
				ReleaseEvent(event);
			}
			
			// Check for a change in this process's frontmost window.
			if(FrontWindow() != front_window)
			{
				ProcessSerialNumber self = { 0, kCurrentProcess };
				ProcessSerialNumber parent = { 0, kNoProcess };
				ProcessSerialNumber front = { 0, kNoProcess };
				Boolean this_is_front_process = false;
				Boolean parent_is_front_process = false;
				{
					// Get this process's parent
					ProcessInfoRec info;
					info.processInfoLength = sizeof(ProcessInfoRec);
					info.processName = NULL;
					info.processAppSpec = NULL;
					if(GetProcessInformation( &self, &info ) == noErr)
					{
						parent = info.processLauncher;
					}
					
					// and figure out whether this process or its parent are currently frontmost
					if(GetFrontProcess(&front) == noErr)
					{
						(void) SameProcess(&self, &front, &this_is_front_process);
						(void) SameProcess(&parent, &front, &parent_is_front_process);
					}
				}
								
				if((FrontWindow() != NULL) && (front_window == NULL))
				{
					// Opening the first window
					
					if(window_hack_state == 0)
					{
						// Next time through the event loop, lower the window group layer
						window_hack_state = 1;
					}

					if(layer_group)
					{
						SetWindowGroup(FrontWindow(), layer_group);
					}
					
					if(parent_is_front_process)
					{
						// Bring this process's windows to the front.
						(void) SetFrontProcess( &self );
					}

					ActivateWindow(FrontWindow(), true);					
				}
				else if((FrontWindow() == NULL) && (front_window != NULL))
				{
					// Closing the last window
					
					if(this_is_front_process)
					{
						// Try to bring this process's parent to the front
						(void) SetFrontProcess(&parent);
					}
				}
				else if(window_hack_state == 1)
				{
					if(layer_group)
					{
						// Set the window group level back to something less extreme
						SetWindowGroupLevel(layer_group, kCGNormalWindowLevel);
					}
					window_hack_state = 2;
				}

				front_window = FrontWindow();

			}
		}
#endif
		F64 elapsed = timer.getElapsedTimeF64();
		F64 remaining = plugin->getSleepTime() - elapsed;

		if(remaining <= 0.0f)
		{
			// We've already used our full allotment.
//			LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, not sleeping" << LL_ENDL;

			// Still need to service the network...
			plugin->pump();
		}
		else
		{

//			LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL;
//			timer.reset();

			// This also services the network as needed.
			plugin->sleep(remaining);

//			LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" <<  LL_ENDL;
		}

#if LL_WINDOWS
	// More agressive checking of interfering exception handlers.
	// Doesn't appear to be required so far - even for plugins
	// that do crash with a single call to the intercept
	// exception handler such as QuickTime.
	//checkExceptionHandler();
#endif

#if LL_DARWIN
		deleteAutoReleasePool();
#endif
	}

	delete plugin;

	ll_cleanup_apr();

	return 0;
}
Exemple #8
0
int main(int argc, char **argv)
#endif
{
	ll_init_apr();

	// Set up llerror logging
	{
		LLError::initForApplication(".");
		LLError::setDefaultLevel(LLError::LEVEL_INFO);
//		LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG);
//		LLError::logToFile("slplugin.log");
	}

#if LL_WINDOWS
	if( strlen( lpCmdLine ) == 0 )
	{
		LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL;
	};

	U32 port = 0;
	if(!LLStringUtil::convertToU32(lpCmdLine, port))
	{
		LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
	};

	// Insert our exception handler into the system so this plugin doesn't
	// display a crash message if something bad happens. The host app will
	// see the missing heartbeat and log appropriately.
	initExceptionHandler();
#elif LL_DARWIN || LL_LINUX
	if(argc < 2)
	{
		LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL;
	}

	U32 port = 0;
	if(!LLStringUtil::convertToU32(argv[1], port))
	{
		LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
	}

	// Catch signals that most kinds of crashes will generate, and exit cleanly so the system crash dialog isn't shown.
	signal(SIGILL, &crash_handler);		// illegal instruction
# if LL_DARWIN
	signal(SIGEMT, &crash_handler);		// emulate instruction executed
# endif // LL_DARWIN
	signal(SIGFPE, &crash_handler);		// floating-point exception
	signal(SIGBUS, &crash_handler);		// bus error
	signal(SIGSEGV, &crash_handler);	// segmentation violation
	signal(SIGSYS, &crash_handler);		// non-existent system call invoked
#endif

	LLPluginProcessChild *plugin = new LLPluginProcessChild();

	plugin->init(port);

	LLTimer timer;
	timer.start();

#if LL_WINDOWS
	checkExceptionHandler();
#endif

#if LL_DARWIN
	EventTargetRef event_target = GetEventDispatcherTarget();
#endif
	while(!plugin->isDone())
	{
		timer.reset();
		plugin->idle();
#if LL_DARWIN
		{
			// Some plugins (webkit at least) will want an event loop.  This qualifies.
			EventRef event;
			if(ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &event) == noErr)
			{
				SendEventToEventTarget (event, event_target);
				ReleaseEvent(event);
			}
		}
#endif
		F64 elapsed = timer.getElapsedTimeF64();
		F64 remaining = plugin->getSleepTime() - elapsed;

		if(remaining <= 0.0f)
		{
			// We've already used our full allotment.
//			LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, not sleeping" << LL_ENDL;

			// Still need to service the network...
			plugin->pump();
		}
		else
		{

//			LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL;
//			timer.reset();

			// This also services the network as needed.
			plugin->sleep(remaining);

//			LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" <<  LL_ENDL;
		}

#if LL_WINDOWS
	// More agressive checking of interfering exception handlers.
	// Doesn't appear to be required so far - even for plugins
	// that do crash with a single call to the intercept
	// exception handler such as QuickTime.
	//checkExceptionHandler();
#endif
	}

	delete plugin;

	ll_cleanup_apr();

	return 0;
}