Solved

How to solve invalid thread access error in Java?

Posted on 2013-11-20
4
1,015 Views
Last Modified: 2013-11-20
Hi,
I am working on creating a GUI using Java SWT. In this GUI, I have a dialog box and this dialog box read some data from a server when you select an item from a drop down list (combo).

Since, the data that I read from the server is big, I need to run this in a separate thread than the main UI thread.

I tried doing this using the example in the link below:

http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet130.java

I did exactly the same in my code but now I am getting "Invalid Thread Access" error.

When I investigate the error, I figured out that the work thread is trying to access a variable in the main thread and this causes the problem. In order to solve this problem I tried to use syncExec. It solves the thread access problem but then the dialog box started to hang again while reading data from the server.

I also tried to use asyncExec but it didn't help as well.

Can you please take a look at the code and give me some suggestions? (I know that the code is not following MVC model but I cannot change it right now. But I will. So, any solution that will solve this problem for now is greatly appreciated)

I commented out the sync.exec in lines 107-109 to reproduce the problem.

The invalid thread access error is in line 112.

private void createContents(final Shell shell) throws ConnectionException, AccessException, RequestException, ConfigException, NoSuchObjectException, ResourceException, URISyntaxException {
    	
    	//Connect to Perforce Server
    	final IOptionsServer p4d = ServerFactory.getOptionsServer(
			        ServerFactory.DEFAULT_PROTOCOL_NAME 
			        + "://" + "someServer", null);
			
			p4d.setUserName("tolgar");			

			p4d.connect();
        

        String userName = System.getProperty("user.name");
        //Get the workspace and changelist information from Perforce
        List<IClientSummary> clientSummaryList = getAllPerforceWorkspaces(p4d, userName);
                
        shell.setLayout(new GridLayout(1, false));

        Composite workspaceComp = new Composite(shell, SWT.NONE);
        workspaceComp.setLayout(new RowLayout());
        
        Label label_workspace = new Label(workspaceComp, SWT.NONE);
        label_workspace.setText("Workspace:");
            
        final Combo combo_workspaces = new Combo(workspaceComp, SWT.READ_ONLY);
        combo_workspaces.setEnabled(true);
        
        
        //public static Table table;
    	final List<Integer> changeListsArray = new ArrayList<Integer>();
    	final List<StringBuilder> fileListArray = new ArrayList<StringBuilder>();
    	final List<String> stateArray = new ArrayList<String>();
    	final List<String> changeListDescriptionsArray = new ArrayList<String>();

        List<String> workspacesArray = new ArrayList<String>();
        final String[] changelistStateArray = {"Pending", "Shelved", "Submitted"};     
        
        //This is to create the data(changelist number, file name, description) in each column using P4JAVA
        final GetChangelistsOptions getoptions = new GetChangelistsOptions();
        for(IClientSummary eachChangeSummary : clientSummaryList){
            String eachClientName = eachChangeSummary.getName();
            workspacesArray.add(eachChangeSummary.getName());
        }
        
        combo_workspaces.setItems(workspacesArray.toArray(new String[workspacesArray.size()]));


        // ----------- the table -----------------
        GridLayout groupLayoutTable = new GridLayout();
        Group groupTable = new Group(shell, SWT.NONE);       
        groupTable.setLayout(groupLayoutTable);        
        
        final Table table = new Table(groupTable, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.WRAP | SWT.MULTI | SWT.CENTER | SWT.CHECK );
        TextCellEditor edit =new TextCellEditor(table,SWT.V_SCROLL);
        GridData tableGridData = new GridData(GridData.FILL_BOTH);
        //tableGridData.horizontalSpan = 2;
        tableGridData.horizontalAlignment = SWT.FILL;
        tableGridData.grabExcessHorizontalSpace = true;
        tableGridData.verticalAlignment = SWT.FILL;
        tableGridData.grabExcessVerticalSpace = true;
        groupTable.setLayoutData(tableGridData);
        
        table.setLinesVisible(true);
        table.setHeaderVisible(true);
        table.getVerticalBar().setVisible(true);
        final String[] titles = { " ", "  Status  ", "Changelist", "Files", "Description" };
        
        //This is to create the column titles
        for (int loopIndex = 0; loopIndex < titles.length; loopIndex++) {
            TableColumn column = new TableColumn(table, SWT.NULL);
            column.setText(titles[loopIndex]);
        }
        
        int frameShellWidth = shell.getSize().x;
        tableGridData.widthHint = frameShellWidth;
        table.setLayoutData(tableGridData);
        
        for (int loopIndex = 0; loopIndex < titles.length; loopIndex++) {
            table.getColumn(loopIndex).pack();
       }
      table.setRedraw(true);
        
        //String selectedWorkspace = combo_workspaces.getText();
        //if(eachClientName.contains(selectedWorkspace)){
      final int[] nextId = new int[1];
        combo_workspaces.addSelectionListener(new SelectionAdapter() {
        	@Override
            public void widgetSelected(SelectionEvent e) {            	
            	Runnable longJob = new Runnable() {
            	boolean done = false;       
            	int id;
				@Override
				public void run() {
					Thread thread = new Thread(new Runnable() {
						@Override
						public void run() {         
								id = nextId[0]++;
								display.syncExec(new Runnable() {
									@Override
									public void run() {
						            	//clear the table
						            	table.removeAll();
						            	table.setRedraw(true);						            	            	
									}
								}); 
								
//								display.syncExec(new Runnable() {
//									@Override
//									public void run() {
							        	for (String changelistState : changelistStateArray){
							        		
							                getoptions.setClientName(combo_workspaces.getText());
							                
							                if (changelistState.equalsIgnoreCase("Pending")){
							                	getoptions.setType(IChangelist.Type.PENDING);
							                } else if (changelistState.equalsIgnoreCase("Shelved")){
							                	getoptions.setType(IChangelist.Type.SHELVED);
							                } else if(changelistState.equalsIgnoreCase("Submitted")){
							                	getoptions.setType(IChangelist.Type.SUBMITTED);
							                }
							                	                
							                List<IChangelistSummary> changeLists = null;
											try {					
												changeLists = p4d.getChangelists(null, getoptions);
											} catch (P4JavaException e1) {
												// TODO Auto-generated catch block
												e1.printStackTrace();
											}
							                //System.out.println(eachChangeSummary.getName() +" : "+eachChangeSummary.getDescription() );
							                    for(IChangelistSummary eachChangeList : changeLists){
							                    	
							                    	StringBuilder depotfiles = new StringBuilder();
							                    	changeListsArray.add(eachChangeList.getId());
							                    	changeListDescriptionsArray.add(eachChangeList.getDescription());
							                    	stateArray.add(changelistState);
							                    	//DEBUG:: System.out.println(eachChangeList.getId()+" - " + eachChangeList.getDescription());
							                    		
							                        IChangelist cinfo = null;
													try {
														cinfo = p4d.getChangelist(eachChangeList.getId());
													} catch (ConnectionException e3) {
														// TODO Auto-generated catch block
														e3.printStackTrace();
													} catch (RequestException e3) {
														// TODO Auto-generated catch block
														e3.printStackTrace();
													} catch (AccessException e3) {
														// TODO Auto-generated catch block
														e3.printStackTrace();
													}
							                        List<IFileSpec> filesList = null;
													try {
														filesList = cinfo.getFiles(true);
													} catch (ConnectionException e2) {
														// TODO Auto-generated catch block
														e2.printStackTrace();
													} catch (RequestException e2) {
														// TODO Auto-generated catch block
														e2.printStackTrace();
													} catch (AccessException e2) {
														// TODO Auto-generated catch block
														e2.printStackTrace();
													}
							                        try {
														filesList = p4d.getChangelistFiles(eachChangeList.getId());
													} catch (ConnectionException e1) {
														// TODO Auto-generated catch block
														e1.printStackTrace();
													} catch (RequestException e1) {
														// TODO Auto-generated catch block
														e1.printStackTrace();
													} catch (AccessException e1) {
														// TODO Auto-generated catch block
														e1.printStackTrace();
													}
							                        for(IFileSpec eachFileSpec : filesList){
							                            //DEBUG:: System.out.println(eachFileSpec.getDepotPathString());
							                            String lineFeed = System.getProperty("line.separator");
							                            if (eachFileSpec.getDepotPathString() != null){
							                            	depotfiles = depotfiles.append(eachFileSpec.getDepotPathString() + lineFeed);
							                            } else{
							                            	depotfiles = depotfiles.append(eachFileSpec.getLocalPathString() + lineFeed);
							                            }
							                        }    
							                        fileListArray.add(depotfiles);
							                    }
							        		}
//									}
//								});
							        	
										display.syncExec(new Runnable() {
											@Override
											public void run() {
								            //This is to populate data in the table
								            for (int loopIndex = 0; loopIndex < changeListsArray.size(); loopIndex++) {
								              TableItem item = new TableItem(table, SWT.NULL);
							              
								              item.setText(1, stateArray.get(loopIndex));
								              item.setText(2, changeListsArray.get(loopIndex).toString());
								              item.setText(3, fileListArray.get(loopIndex).toString());
								              item.setText(4, changeListDescriptionsArray.get(loopIndex));
								              //if (loopIndex % 2 == 0) item.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
								            }
								            
								            for (int loopIndex = 0; loopIndex < titles.length; loopIndex++) {
								            	table.getColumn(loopIndex).pack();
								            }
							            
							            table.setRedraw(true);
											}
										});
										
								        final boolean processCompleted = true;
							            //clears the table
							            cleanList(changeListsArray, fileListArray, stateArray, changeListDescriptionsArray, table);
							            
							            
										if (display.isDisposed()) return;
										display.syncExec(new Runnable() {
											@Override
											public void run() {
												if (processCompleted){
												//text.append("\nCompleted long running task "+id);
												System.out.println("Completed long running task");
												}else{
													return;
												}
											}
										});
										done = true;
										display.wake();
							            
										display.syncExec(new Runnable() {
											@Override
											public void run() {
								        //This is to sort the columns in the table
							            Listener sortListener = new Listener() {
							                int prevSortIndex = -1;
							                int prevSortDirection = 1;
						                    int prevSortDir;
						                    
							                public void handleEvent(Event e) {
							                    TableItem[] items = table.getItems();
							                    Collator collator = Collator.getInstance(Locale.getDefault());
							                    TableColumn column = (TableColumn) e.widget;
							                    int index = column == table.getColumn(0) ? 0 : 1;
							                    if (prevSortIndex == index) {
							                        prevSortDir *= -1;
							                    } else {
							                        prevSortDir = 1;
							                        prevSortIndex = index;
							                    }
							                    for (int i = 1; i < items.length; i++) {
							                        String value1 = items[i].getText(index);
							                        for (int j = 0; j < i; j++) {
							                            String value2 = items[j].getText(index);
							                            if ((collator.compare(value1, value2) * prevSortDir) < 0) {
							                                String[] values = { items[i].getText(0),
							                                        items[i].getText(1), items[i].getText(2),
							                                        items[i].getText(3) };
							                                items[i].dispose();
							                                TableItem item = new TableItem(table, SWT.NONE, j);
							                                item.setText(values);
							                                items = table.getItems();
							                                break;
							                            }
							                        }
							                    }
							                    table.setSortColumn(column);
							                }
							            };
							            
							            
							            table.getColumn(1).addListener(SWT.Selection, sortListener);
							            table.getColumn(2).addListener(SWT.Selection, sortListener);
							            table.setSortColumn(table.getColumn(1));
							            table.setSortDirection(SWT.TOP);							            
							            table.setRedraw(true);	
										}
									});
//										}
//									});	//sync.exec				        					        					            
								} //second public void run									
							}); //new Thread
							thread.start();
							while (!done && !shell.isDisposed()) {
								if (!display.readAndDispatch())
									display.sleep();
							}
					}
            	}; //first public void run
					BusyIndicator.showWhile(display, longJob);
            } //widgetSelected  
          }); //SelectionAdapter
        
					
					
        
        //Get the checked changelists from the table
        table.addListener(SWT.Selection, new Listener()
        {
            @Override
            public void handleEvent(Event event)
            {
                if(event.detail == SWT.CHECK)
                {
                    TableItem current = (TableItem)event.item;
                    String lineFeed = System.getProperty("line.separator");
                    String fileFromChangelist = current.getText(3).replace(lineFeed, "") + " #### " + "-c " + current.getText(2) + " -" + current.getText(1).toLowerCase();
                    if(current.getChecked())
                    {                    	
                    	changelistNum = changelistNum + fileFromChangelist;
                        //DEBUG:: System.out.println(current.getText(2));
                    } else{
                    	String removeIt = fileFromChangelist;
                    	changelistNum = changelistNum.replace(removeIt, "");
                    }
                }
            }
        });
        

        Composite buttonComp = new Composite(shell, SWT.NONE);
        buttonComp.setLayout(new RowLayout());
        
        Button ok = new Button(buttonComp, SWT.PUSH);
        ok.setText("Add files from the selected changelist(s)");
        ok.addSelectionListener(new SelectionAdapter() {
          public void widgetSelected(SelectionEvent event) {
            if (!changelistNum.isEmpty()){
                    
            	populatechangelist(changelistNum);
	            shell.close();	
	            //disconnect from the Perforce server
	            disconnectFromServer(p4d);
	            changelistNum = "";
            }else{
            	shell.close();
                //disconnect from the Perforce server
            	disconnectFromServer(p4d);
            }
          }
        });

        Button cancel = new Button(buttonComp, SWT.PUSH);
        cancel.setText("Cancel");
        cancel.addSelectionListener(new SelectionAdapter() {
          public void widgetSelected(SelectionEvent event) {
            shell.close();
          }
        });
        shell.setDefaultButton(ok);
        
    }
    
    public void disconnectFromServer(IOptionsServer p4d){
        //disconnect from the Perforce server
        try {
			p4d.disconnect();
		} catch (ConnectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }

    
    
    public void populatechangelist(String changeLists){
        if (Gui.txt.getText().isEmpty()){
            Gui.txt.setText(Gui.txt.getText() + changeLists);
        } else{
            Gui.txt.setText(Gui.txt.getText() + "\r\n" +  changeLists);
        }
    }
    

    public static void cleanList(List<Integer> changeListsArray, List<StringBuilder> fileListArray, List<String> stateArray, List<String> changeListDescriptionsArray, Table table){
    	changeListsArray.removeAll(changeListsArray);
    	fileListArray.removeAll(fileListArray);
    	stateArray.removeAll(stateArray);
    	changeListDescriptionsArray.removeAll(changeListDescriptionsArray);
    	//table.setRedraw(true);
    }
    
}

Open in new window

0
Comment
Question by:Tolgar
  • 2
  • 2
4 Comments
 
LVL 35

Accepted Solution

by:
mccarl earned 500 total points
Comment Utility
Ok, so you need to follow some rules...

Any GUI access (read or write) needs to either happen before starting the new thread, or from within that display.asyncExec() call. And generally, you can extend this to, do all reads of GUI items before you start the thread, and then do all writes/updates of GUI items at the end of the thread within the display.asyncExec() call. But try and keep the long running operations, in the new thread but out of (ie. before) the display.asyncExec() call.

In light of the above, to fix the issue on line 112, and a few others that I can see from a quick look...

On line 112, you are trying to "read" the value of the combobox, so as per above, you just need to do that before you start the thread. Also, because this will be outside the anonymous block of code that is the thread, you will need to make this 'final', so add the following line between 88 and 89 of the above... final String selectedWorkspace = combo_workspaces.getText();          Then on line 112, you can use 'selectedWorkspace' to set the client name.
Following similar lines, move all GUI operations the should happen before the long operation out of the new thread, so move lines 103 & 104 up to between 88 and 89 aswell (and get rid of the syncExec that is around it)
The display.asyncExec() call should wrap just the table updating code towards the end of this snippet. But it looks like you have this correct (it's hard to tell 100% though because the indentation is a bit messed up ;)

Hopefully, that is enough to get you going!
0
 

Author Comment

by:Tolgar
Comment Utility
This worked! Great!
0
 

Author Comment

by:Tolgar
Comment Utility
Well, there is slight problem.

When I close the widget while the process is running (and I repeated this a few times) at some point, I got the following error:

Exception in thread "Thread-1" org.eclipse.swt.SWTException: Failed to execute runnable (org.eclipse.swt.SWTException: Widget is disposed)

Open in new window


The error message shows line 191.

But before that the other lines that caused the problem are:
191 
196 
287
298

Open in new window



And line 49 of the following code snippet (in the same class):

   public void open() {
        Shell shell = new Shell(getParent(), getStyle());
        shell.setText("Select changelist");
        try {
			createContents(shell);
		} catch (ConnectionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (AccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (RequestException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ConfigException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchObjectException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ResourceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}               
        
        shell.pack();
        shell.open();
        
        
        int frameX = shell.getSize().x - shell.getClientArea().width;
        int frameY = shell.getSize().y - shell.getClientArea().height;
        shell.setSize(500 + frameX, 300 + frameY); 
        
        
     // Move the dialog to the center of the top level shell.
        Rectangle shellBounds = getParent().getBounds();
        Rectangle dialogSize = shell.getBounds();

        shell.setLocation(
          shellBounds.x + (shellBounds.width - dialogSize.width) / 2,
          shellBounds.y + (shellBounds.height - dialogSize.height) / 2 );
             
        
        display = getParent().getDisplay();
        while (!shell.isDisposed()) {
          if (!display.readAndDispatch()) {
            display.sleep();
          }
        }
    }

Open in new window


Why do you think I am getting this  error if I close the window multiple times while the process is running?
0
 
LVL 35

Assisted Solution

by:mccarl
mccarl earned 500 total points
Comment Utility
Basically, the problem is that the server retrieval IS still happening, in that new thread (despite you closing the dialog box, the thread still runs) and so when you get to the point where you try to update the table, the table is no longer there.

This is the reasoning behind the various .isDisposed() method calls in the example in the link that I posted above. So you probably should also add a similar check for if your table.isDisposed() at about line 194 of the original code that you posted above, so that you don't execute all the stuff that adds items to the table, if it has been disposed, ie. the dialog box closed.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
Video by: Michael
Viewers learn about how to reduce the potential repetitiveness of coding in main by developing methods to perform specific tasks for their program. Additionally, objects are introduced for the purpose of learning how to call methods in Java. Define …
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now