Debuger i błąd Access violation reading location 0x00000000

0

Witam.

W pętli wykonuje mi się kod.

Kawałek tego kodu:


	if(closestCreature)
		{
			if(closestCreature->isDead())
			{
				closestCreature = NULL;
			}
			else if(!yourself->canSee(closestCreature))
			{
				closestCreature = NULL;
			}

			else if(!yourself->isReachable(closestCreature))
			{
				closestCreature = NULL;
			}
		}

Moj program to aplikacja kilkowątkowa.

Gdy program działa. To ok. po 2-8 godzinach debuger pokazuje błąd błąd:

Access violation reading location 0x00000000

na linijce:

else if(!yourself->isReachable(closestCreature))

Jak to możliwe?

przecieŻ wczesniej też jest użyty wskaźnik yourself.

Myślałem, że inny wątek po prostu "niechcący" usuwa wskaźnik yourself akurat w tym czasie.

Jednak to raczej nie możliwe. Bo yourself to obiekt globalny na całą aplikacje, a poza tym ten błąd zawsze występuje w tym samym miejscu.

Z góry dziękuje za pomoc

0

wielowątkowość jest tu najistotniejszym problemem!
Sorry ale nie jesteśmy ci w stanie pomóc. Najwyraźniej masz problem z właściwą synchronizacją wątków, względnie któryś wątek używa nie prawidłowego wskaźnika. Baz wiedzy jakie masz wątki, co one robią, jakie są miedzy nimi zależności nie da się tego przeanalizować i naprawić.
Na dodatek nie wiadomo, co siedzi w tych metodach i tam może coś się dzieje.

0

Ale gdyby to się działo w tej metodzie, to chyba pokazało by mi błąd tam, a nie przy wywołaniu?

0

Niekoniecznie, jeśli wcześniejsza (canSee) metoda modyfikuje pośrednio tą zmienną (pośrednio bo this nie da się zmienić), to dopiero w momencie posłużenia się tym wskaźnikiem wystąpi błąd.
Poza tym, jeśli isReachable jest niedebugowalna (tak ustawiona kompilacja modułu) to błąd zostanie wskazany na zewnątrz tej metody.

Stawiam jednak, że coś pomieszałeś z wątkami skoro błąd pojawia się po 2-8 godzinach. Coś źle synchronizujesz i w 1 przypadku na 10bilionów razy coś się źle robi. Przeanalizuj współdzielone zmienne wątków i czy są dobrze zabezpieczone.

0

Teraz zauważyłem, że ten błąd jest co ok 20 minut i teraz pokazuje go w wewnątrz przeciążonej funkcji.

pokazuje go w :

return isReachable(thing,FLAG_IGNOREBLOCKINGDEST); // tutaj w tej linijce jest błąd

w tym kodzie

bool Creature::isReachable(Thing* thing)
{
	return isReachable(thing,FLAG_IGNOREBLOCKINGDEST); // tutaj w tej linijce jest błąd
}

bool Creature::isReachable(Thing* thing, UINT32 flags)
{
	if(!thing) return false;

	if(thing->getParent() && thing->getParent()->getTile())
	{
		if(map->getPathTo(this,&thing->getParent()->getTile()->pos,flags))
			return true;
	}

	return false;
}

bool Creature::isReachable(Position& pos)
{
	return isReachable(pos,FLAG_IGNOREBLOCKINGDEST);
}

bool Creature::isReachable(Position& pos, UINT32 flags)
{
	if(Tile* tile = map->GetTile(pos))
	{
		if(map->getPathTo(this,&pos,flags))
			return true;
	}

	return false;
}
0

Szczerze mówiąc to sesje krytyczne używam tylko w jednej (najczęściej używanej) klasie. A klas mam w aplikacji chyba z 30.

: P

Ehh, troche ciężko będzie dodać nagle sesje krytyczne do kilkunasto tysięczno linijkowej aplikacji, którą pisałem przez pół roku 7 h na dobe...

W sumie dlaczego nigdzie indziej nie ma błedu tylko zawsze w tym samym miejscu? a tą funkcje używam w różnych miejscach. : /

0

Sorry, ale jak się pisze program, który ma być z założenia wielowątkowy to synchronizacja współdzielonych zmiennych jest obowiązkowa. Takie podejście, a działa sobie to zostawmy to w spokoju to czysta naiwność .
Niestety programowanie wielowątkowe ma tą niemiłą wadę, że testowanie aplikacji jest niedeterministyczne. Program może działać lub się wieszać/crush'ować zależnie od komputera, kompilatora, wersji debug albo release itd itp.

0

Sprawdź dokładnie co ma adres 0. Z tego co podałeś to właśnie wskaźnik yourself jest zerowy.
Sprawdź co w momencie crashu robią inne wątki, debugger powinien wiele powiedzieć. Sprawdź wszystkie miejsca, gdzie yourself może być modyfikowane.

0

Ok, zaraz odpale debugera i aplikacje i zostawie na jakiś czas.

Btw. Zaczołem wstawiać sesje krytyczne.

W każdym obiekcie będe miał pole o typie CRITICAL_SECTION.

zrobiłem sobie makra

  • THREAD_LOCK("wskaźnik na zmienną CRITICAL_SESSION") jako EnterCriticalSession
  • THREAD_UNLOCK("wskaźnik na zmienną CRITICALSESSION") jako LeaveCriticalSession

w każdej funkcji gdzie są odczytywane/zapisywane składowe obiektu this wstawiam na początku THREAD_LOCK.

i chyba musze dawać THREAD_UNLOCK przy każdym return ? : /

to w takim razie popsuje bardzo estetyczność kodu.

Jak powiedzmy w funkcji jest kilkanaście return

i przy każdym będzie

THREAD_UNLOCK(this->lock);
return false;

THREAD_UNLOCK(this->lock);
return object;

THREAD_UNLOCK(this->lock);
return 6;

itd...

0

Po co się tak bawić, użyj RAII.

0

w każdej funkcji gdzie są odczytywane/zapisywane składowe obiektu this wstawiam na początku THREAD_LOCK.
Można i tak, ale to raczej zbyt "głęboko" poszedłeś z tym lockiem. Nie wiem jak to wygląda dokładnie u ciebie, ale zabezpieczona powinna tu być raczej zmienna yourself. Poza tym zabezpieczanie na ślepo wszystkich odczytów/zapisów jest równie złe jak ich całkowite niezabezpieczanie. Może prowadzić do zatrzasków (deadlock), może też nie rozwiązać problemu w ogóle gdyż np. dany zasób powinien być zablokowany przez kilka odczytów/zapisów. Rób to z głową. Niestety będziesz się jeszcze sporo musiał napracować przy tym. Za błędy popełnione na wczesnym etapie tworzenia płaci się słono.
Na początek zabezpiecz zmienną globalną yourself (tylko z głową !).

0

Wziąłem się za zabezpieczanie i powiem, że nawet nie jest tak źle jak myślałem, wstawiam zabezpieczenia tylko tam gdzie jest używane np yourself itd. (zapisywane są tylko w jednym wątku), (a odczytywane w drugim)

tylko mam problem powiedzmy jak używam pewną funkcję w yourself, w której jest pętla i czeka ona aż wątek zapisujący zmieni pewną rzecz. ta funkcja może trwać nawet parenaście sekund.

To na pewno nie ma sensu robienia czegoś takiego

// W wątku czytającym - używanie pewnej funkcji yourself

THREAD_LOCK(protocol->lock);

yourself->Move(pos);

THREAD_UNLOCK(protocol->lock);

W tym przypadku lock trwałby kilkanaście sekund : /

A nie chce używać locków w obiektach, tylko tam gdzie używam tych obiektów

0
THREAD_LOCK(protocol->lock);
wsk = yourself;
THREAD_UNLOCK(protocol->lock);
wsk->Move(pos);

Poza tym czy lok musi trać te kilkanaście sekund - nie wiem. Może musi ? Zależy od kodu.
I widzę, że nie robisz tego z głową. Takie coś nie za wiele da a nawet narobi problemów. Opakowanie z osobna w lock'a każdego wywołania nie jest rozwiązaniem.

0

ale w funkcji move tez są usuwany składowe obiektu yourself, więc co da jeżeli zabezpiecze tylko pobranie wskaźnika yourself jak tam w środku nie było by locków?

0

No właśnie. Musisz się dobrze zastanowić co wymaga zabezpieczenia i na jak długo.

0

Ja bym polecił http://en.wikipedia.org/wiki/Software_transactional_memory .
Masz tam na dole linki do różnych bibliotek.
Przy tak dużym programie lepiej by chyba było zmienić kod maszynowo - nawet pisząć parser c++ (antlr/itd - mają gotowy) i tylko określać gdzie ma zmienić.

0

testowy -- juz Ci to mowilem kiedys, sie powtorze wiec:

brak sensownej architektury nie pozwala Ci sensownie rozwijac i utrzymywac tego co masz
bedac bogatszym o zdobyta wiedze, napisz calosc jeszce raz, wpierw przemyslawszy dokladnie jak wszystko ma wygladac i dzialac.
zajmie Ci to mniej czasu niz straciles do tej pory na szukanie bledu
a bedac bardziej doswiadczonym, napiszesz to tak, zeby potem bylo Ci latwiej sie z tym obchodzic..

i zdecyduj sie w koncu na jeden nick

1 użytkowników online, w tym zalogowanych: 0, gości: 1